修复此模板分辨率模糊的正确方法是什么?

时间:2016-03-25 14:03:10

标签: c++ c++11 c++14 sfinae enable-if

假设我写了:

short

当我编译它时,我得到一个关于调用歧义的错误(如果我用float替换foo(),则没有错误)。我应该如何修复此代码,以便获得整数类型的较高版本,否则为较低版本?

如果您的建议除了普通版本之外,还会扩展到DECLARE @sourceLatitude FLOAT = 53.0150594250908; DECLARE @sourceLongitude FLOAT = -2.24460456782419; DECLARE @destinationLatitude FLOAT = 52.002933355733400; DECLARE @destinationLongitude FLOAT = -0.976678285584733; DECLARE @Location FLOAT SET @Location = SQRT(POWER(69.1 * ( @destinationLatitude - @sourceLatitude), 2) + POWER(69.1 * ( @sourceLongitude - @destinationLongitude ) * COS(@destinationLatitude / 57.3), 2)) PRINT @Location 的多个专业版本的情况,则会获得积分。

5 个答案:

答案 0 :(得分:15)

我喜欢Xeo's approach来解决这个问题。让我们做一些带有后备的标签调度。创建一个从其自身继承的选择器结构:

template <int I>
struct choice : choice<I + 1> { };

template <> struct choice<10> { }; // just stop somewhere

因此choice<x>可转换为choice<y> x < y,这意味着choice<0>是最佳选择。现在,你需要最后一个案例:

struct otherwise{ otherwise(...) { } };

使用该机制,我们可以使用额外的参数转发我们的主函数模板:

template <class T> void foo() { foo_impl<T>(choice<0>{}); }

然后让你的顶级选择积分和最糟糕的选项...任何事情:

template <class T, class = std::enable_if_t<std::is_integral<T>::value>>
void foo_impl(choice<0> ) {
    std::cout << "T is integral." << std::endl;
}

template <typename T>
void foo_impl(otherwise ) {
    std::cout << "Any T." << std::endl;
}

这使得在中间添加更多选项变得非常容易。只需为choice<1>choice<2>或其他内容添加重载。也不需要不相交的条件。 choice<x>的优先重载解决方案可以解决这个问题。

如果你另外传递T作为参数,那就更好了,因为重载比专业更好:

template <class T> struct tag {};
template <class T> void foo() { foo_impl(tag<T>{}, choice<0>{}); }

然后你可以疯狂:

// special 1st choice for just int
void foo_impl(tag<int>, choice<0> );

// backup 1st choice for any integral
template <class T, class = std::enable_if_t<std::is_integral<T>::value>>
void foo_impl(tag<T>, choice<0> );

// 2nd option for floats
template <class T, class = std::enable_if_t<std::is_floating_point<T>::value>>
void foo_impl(tag<T>, choice<1> );

// 3rd option for some other type trait
template <class T, class = std::enable_if_t<whatever<T>::value>>
void foo_impl(tag<T>, choice<2> );

// fallback 
template <class T>
void foo_impl(tag<T>, otherwise );

答案 1 :(得分:9)

使用标签分派(C ++ 11)的另一个选项:

#include <iostream>

void foo_impl(std::false_type) {
    std::cout << "Any T." << std::endl;
}

void foo_impl(std::true_type) {
    std::cout << "T is integral." << std::endl;
}

template <typename T>
void foo() {
    foo_impl(std::is_integral<typename std::remove_reference<T>::type>());
  //foo_impl(std::is_integral<typename std::remove_reference_t<T>>()); // C++14
}

int main() { 
    foo<short>();  // --> T is integral.
    foo<short&>(); // --> T is integral.
    foo<float>();  // --> Any T.
}

借用Scott Meyers Effective Modern C ++ 第27项。

答案 2 :(得分:5)

一种方式:

template <typename T, typename std::enable_if_t<std::is_integral<T>::value>* = nullptr>
void foo() { std::cout << "T is integral." << std::endl; }

template <typename T, typename std::enable_if_t<not std::is_integral<T>::value>* = nullptr>
void foo() { std::cout << "Any T." << std::endl; }

另一种方法是推迟到模板函数对象:

template<class T, typename = void>
struct foo_impl
{
    void operator()() const {
        std::cout << "Any T." << std::endl;
    }
};

template<class T>
struct foo_impl<T, std::enable_if_t<std::is_integral<T>::value>>
{
    void operator()() const {
        std::cout << "T is integral." << std::endl;
    }
};

template<class T>
void foo() {
    return foo_impl<T>()();
}

答案 3 :(得分:4)

一种方法是:

template <typename T>
std::enable_if_t<std::is_integral<T>::value, void> foo () {
    std::cout << "integral version" << std::endl;
}

template <typename T>
std::enable_if_t<!std::is_integral<T>::value, void> foo () {
    std::cout << "general version" << std::endl;
}

用法:

foo<int> ();
foo<double> ();
struct X {};
foo<X> ();

输出是:

integral version
general version
general version

答案 4 :(得分:3)

AFAIK,sfinae适用于函数参数,因此尝试使用默认值添加依赖类型的参数

template <typename T>
void foo(typename std::enable_if_t<std::is_integral<T>::value>* = 0) 
{ std::cout << "T is integral." << std::endl; }

template <typename T>
void foo(typename std::enable_if_t<!std::is_integral<T>::value>* = 0) 
{ std::cout << "Any T." << std::endl; }