我正在尝试创建一个模板化函数,强制执行编译时只使用特化。我引用了Force a compile time error in a template specialization,建议对从static_assert
继承的内容使用std::false_type
。
#include <iostream>
using namespace std;
template<typename T>
struct always_false : std::false_type {};
//Case: Default
template<typename T>
void foo(T val) {
static_assert(always_false<T>::value, "");
}
//Case: bool
template<>
void foo<bool>(bool val) {
cout << "Is explicitly a bool! " << val << endl;
}
//Case: int
template<typename T, typename std::enable_if<!std::is_same<T,bool>::value && std::is_convertible<T,int>::value,int>::type=0>
void foo(T val) {
cout << "Can be implicitly converted to int! " << (int)val << endl;
}
int main() {
foo(true); //(Good) Works correctly
foo((int)5); //(Bad) Error: call of overload foo(int) is ambiguous
foo((unsigned int)10); //(Bad) Error: call of overload foo(unsigned int) is ambiguous
foo((void*)nullptr); //(Good) Error: static assertion failed
return 0;
}
当我传入int
或unsigned int
时,编译器会抱怨该调用含糊不清,表明它可以使用Case: Default
或Case: int
。
这很令人困惑,因为Case: Default
有always_false
static_assert()
,我希望编译器不允许它。
我传递void*
的最后一个示例成功触发static_assert()
并导致编译时错误。
我是使用SFINAE模板元编程进行编程的新手,所以我怀疑我在Case: int
专业化中做错了什么
两个问题:
foo(int)
不明确?bool
特化+隐式整数特化)?答案 0 :(得分:1)
为什么这段代码中的foo(int)不明确?
因为带有static_assert()
的版本在选中但仍然存在时会出错;因此,编译器不知道是选择通用版本还是启用整数版本。
有没有更好的方法来使用模板来获得所需的行为(显式bool特化+隐式int特化)?
一种可能的方法是避免使用通用版本,SFINAE启用您需要的版本
以下是一个完整的工作示例
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_same<T, bool>::value>::type foo(T val)
{ std::cout << "bool case " << val << std::endl; }
template <typename T>
typename std::enable_if< ! std::is_same<T, bool>::value
&& std::is_convertible<T, int>::value>::type foo(T val)
{ std::cout << "integer case " << (int)val << std::endl; }
int main()
{
foo(true); // bool case
foo(1); // integer case
foo(2U); // integer case
foo(3L); // integer case
foo(4UL); // integer case
foo(5LL); // integer case
foo(6ULL); // integer case
// foo((void*)nullptr); // compilation error
}
- 编辑 -
OP
抱歉,我仍然感到困惑。你能详细说说吗?我认为由于SFINAE,如果在替换中发生错误,它将使用另一个模板。
完全。 问题是当没有替换错误时,编译器必须在同一模板的两个不同版本之间进行选择。
我的意思是:在您的示例中,当您致电foo(5)
时,没有替代
typename std::enable_if<!std::is_same<T,bool>::value
&& std::is_convertible<T,int>::value,int>::type=0>
因此编译器必须在两个模板函数之间进行选择
template<typename T>
void foo(T val) {
static_assert(always_false<T>::value, "");
}
//Case: int
template<typename T, int = 0>
void foo(T val) {
cout << "Can be implicitly converted to int! " << (int)val << endl;
}
仅对具有默认值的模板值有所不同,因此(从编译器的角度来看)是难以区分的。
并观察
template<>
void foo<bool>(bool val) {
cout << "Is explicitly a bool! " << val << endl;
}
是(完整)模板专业化但
//Case: int
template<typename T, int = 0>
void foo(T val) {
cout << "Can be implicitly converted to int! " << (int)val << endl;
}
不是模板特化(在C ++ 11/14/17中不允许对函数进行部分模板特化;您可以仅部分地专门化结构/类);是一个通用模板。
答案 1 :(得分:0)
你可以按照@ max66的建议使用SFINAE,但对你的用例来说,一个简单的方法就是bool
重载和模板化版本
void foo(bool);
template <class T>
void foo(T);
您可以强制T
可转换为int
(static_assert
)但在大多数情况下没有必要,因为foo
的正文可能会格式不正确在这种情况下,从而导致编译时错误。
template <class T>
void foo(T) {
static_assert(std::is_convertible<T, int>::value, "");
}
举例:
foo(true); // foo(bool) is chosen because it is the best match
foo((int)5); // foo<int>(int) is chosen, the assertion passes
foo((unsigned int)10); // foo<unsigned int>(unsigned int) is chosen, assertion ok
foo((void*)nullptr); // foo<void*>(void*) is chosen, the assertion fails