C++ 我能在重载模板函数和约束更严格的模板函数之间消除歧义吗? 代码 #包括 #包括 模板 T foo(T){return T;} 模板 void foo(T){} int main() { 傅(5),; } 错误 main.cpp:在函数“int main()”中: main.cpp:14:15:错误:重载“foo(int)”的调用不明确 傅(5),; ^ main.cpp:14:15:注:候选人为: main.cpp:7:3:note:tfoo(T)[其中T=int;=void] T foo(T){return T;} ^ main.cpp:10:6:注:void foo(T)[带T=int] void foo(T){} ^ 问题

C++ 我能在重载模板函数和约束更严格的模板函数之间消除歧义吗? 代码 #包括 #包括 模板 T foo(T){return T;} 模板 void foo(T){} int main() { 傅(5),; } 错误 main.cpp:在函数“int main()”中: main.cpp:14:15:错误:重载“foo(int)”的调用不明确 傅(5),; ^ main.cpp:14:15:注:候选人为: main.cpp:7:3:note:tfoo(T)[其中T=int;=void] T foo(T){return T;} ^ main.cpp:10:6:注:void foo(T)[带T=int] void foo(T){} ^ 问题,c++,c++11,C++,C++11,有可能解决代码中的错误吗?使用SFINAE约束函数不会引入排序,它只意味着约束函数可调用或不可调用,因此当启用第一个重载时,您有两个不明确的函数 最简单的解决方案是使用同一约束的逆来禁用第二个重载,这样对于给定类型只有一个重载是可行的。要实现这一点,您需要将enable_if移动到返回类型,而不是默认模板参数(因为默认模板参数不是函数签名的一部分,因此不能用于重载,只能使用SFINAE进行约束)。这种解决方案不适用于大量重载,除非您可以为每个重载提供一个单独的谓词(即类型特征的组合),从而使任何

有可能解决代码中的错误吗?

使用SFINAE约束函数不会引入排序,它只意味着约束函数可调用或不可调用,因此当启用第一个重载时,您有两个不明确的函数


最简单的解决方案是使用同一约束的逆来禁用第二个重载,这样对于给定类型只有一个重载是可行的。要实现这一点,您需要将
enable_if
移动到返回类型,而不是默认模板参数(因为默认模板参数不是函数签名的一部分,因此不能用于重载,只能使用SFINAE进行约束)。这种解决方案不适用于大量重载,除非您可以为每个重载提供一个单独的谓词(即类型特征的组合),从而使任何参数类型都不匹配多个谓词。这意味着谓词必须是互斥的,您不能让一个谓词测试一个概念(如有符号整数类型),而另一个谓词测试一个概念(如整数类型),因为这两个谓词对于
int
都是真的,并且会启用多个函数


< C++概念是比SFIEAE更好的解决方案的一个重要原因是,概念约束的函数是有序的,因此约束函数比约束较少的函数更好。这意味着您不需要玩这些SFINAE游戏,也不需要相互排斥的约束,编译器会做正确的事情。

将相同的SFINAE应用于替代方法,只需将其反向(并将其移动到返回类型);i、 e.禁用另一个

template<typename T>
typename std::enable_if<std::is_pod<T>::value, T>::type
foo(T t) { return t; }

template<typename T>
typename std::enable_if<!std::is_pod<T>::value, void>::type
foo(T t) { }
模板
typename std::enable_if::type
foo(T){返回T;}
模板
typename std::enable_if::value,void>::type
foo(T){}
您提到会有很多重载,所以这可能会很麻烦。。。您的里程可能会有所不同


注意,为什么只有2个?最初的问题是关于2,但这里的问题更多的是涉及模板约束的许多重载之间的排序。这个答案是对一个更经典的解决方案的描述,当只有少量重载时(具有更易于管理的SFINAE约束)。

最简单的解决方案是在第一个函数受到约束时禁用第二个函数

更一般地说,您可以使用重载排序来选择一个特定的函数,该函数的伸缩性更好。是这样的:

template<int N> struct choice : choice<N - 1> {};
template<> struct choice<0> {};
模板结构选择:选择{};
模板结构选择{};
这里,编译器必须执行N个派生到基的转换,才能从N到0。所以我们可以将重载从N到0进行排序,其中N是最理想的,0是最不理想的

template<typename T,
         typename = typename std::enable_if<std::is_pod<T>::value>::type
         >
T foo(T t, choice<1>) { return t; }

template<typename T>
void foo(T t, choice<0>) { }

int main()
{
    foo<int>(5, choice<1>());
}
模板
T foo(T,choice){return T;}
模板
void foo(T,choice){}
int main()
{
foo(5,choice());
}

现在您不必执行O(n^2)条件复制。

启用\u if
移动到返回类型,并使用反向约束

template <typename T>
auto foo(T t) -> typename std::enable_if<std::is_pod<T>::value, T>::type
{   
    return t;
}

template <typename T>
auto foo(T t) -> typename std::enable_if<!std::is_pod<T>::value, void>::type
{
}

int main()
{
    int i = foo<int>(1);
}
模板
自动foo(T)->typename std::enable\u if::type
{   
返回t;
}
模板
自动foo(T)->typename std::enable_if::value,void>::type
{
}
int main()
{
int i=foo(1);
}

@Niall我不知道前面所有可能的限制。实际上,我有更多的重载。另请参见:由于其中一个函数返回
void
,而另一个函数返回
T
,我不知道如何在通用上下文中使用此重载,例如在您编写的通用算法中
T=foo(T{})它不适用于非POD,因为它返回void。为什么不把这两个函数分别命名呢?如果这两个函数返回<代码> t>代码>,这个问题仍然有效吗?C++注释的有趣注释。我认识的大多数人只是认为它只是SFINAE的语法糖,与生的SFINAE没有真正的区别。不知道不是这样。为什么默认模板参数是类类型(std::vector和分配器)的一部分,而不是函数签名的一部分?@TemplateRex,它们不是类类型的一部分,在这两种情况下,它们都是语法糖,允许您省略模板参数,但是默认参数不影响最终类型。请考虑指针类型<代码> FoO*<代码>。类模板
foo
是否具有默认模板参数不是该类型的一部分。如果是,我需要表示它以形成指向该类型的指针。这是一个简单的解决方案,但我的问题确实更一般,因此“排名重载”是这里的正确答案。但是不要删除你的。同意,大量的重载会使SFINAE变得麻烦。如果排序问题失败,您还可以尝试使用具有专门性(部分或全部)的
s来控制函数的分辨率。不过,里程数可能会有所不同。
template<typename T,
         typename = typename std::enable_if<std::is_pod<T>::value>::type
         >
T foo(T t, choice<1>) { return t; }

template<typename T>
void foo(T t, choice<0>) { }

int main()
{
    foo<int>(5, choice<1>());
}
template <typename T>
auto foo(T t) -> typename std::enable_if<std::is_pod<T>::value, T>::type
{   
    return t;
}

template <typename T>
auto foo(T t) -> typename std::enable_if<!std::is_pod<T>::value, void>::type
{
}

int main()
{
    int i = foo<int>(1);
}