Lyn*_*lds 2 c++ templates template-meta-programming c++11
TL; DR:
为什么模板化函数不能访问非模板化函数可以进行的相同转换?
struct A {
A(std::nullptr_t) {}
};
template <typename T>
A makeA(T&& arg) {
return A(std::forward<T>(arg));
}
void foo() {
A a1(nullptr); //This works, of course
A a2(0); //This works
A a3 = makeA(0); //This does not
}
Run Code Online (Sandbox Code Playgroud)
背景
我正在尝试编写一些模板化的包装类来使用现有类型,其目标是直接替换,只需要重写使用现在包装值的现有代码.
我无法理解的一个特殊情况如下:我们有一个可以从std::nullptr_t(这里称为A)构造的类,因此,代码库中有很多地方,有人为实例分配了零.
但是,尽管转发了构造函数,但不能为包装器分配零.我做了一个非常相似的例子,在不使用实际的包装类的情况下重现问题 - 一个简单的模板化函数足以显示问题.
我想允许继续允许分配零的语法 - 这不是我的最爱,但最小化摩擦以转移到更新的代码通常是让人们使用它们的必要条件.
我也不想添加一个构造函数,它接受除零以外的任何int,因为这非常荒谬,之前从未被允许过,并且它应该继续在编译时被捕获.
如果这样的事情不可能,我会找到一个解释,因为我知道到目前为止,对我来说没有任何意义.
这个例子在VC++中具有相同的行为(尽管智能感知它可以......),Clang和GCC.理想情况下,解决方案也适用于所有3个(4个智能感知)编译器.
更直接适用的示例如下:
struct A {
A(){}
A(std::nullptr_t) {}
};
template <typename T>
struct Wrapper {
A a;
Wrapper(const A& a):a (a) {}
template <typename T>
Wrapper(T&& t): a(std::forward<T>(t)){}
Wrapper(){}
};
void foo2() {
A a1;
a1 = 0; // This works
Wrapper<A> a2;
a2 = 0; //This does not
}
Run Code Online (Sandbox Code Playgroud)
为什么编译器决定将零视为int?
因为它是一个整数.
文字0是文字.文字可以做有趣的事情.字符串文字可以转换为const char*或const char[N],N字符串+ NUL终止符的长度.文字0也可以做有趣的事情; 它可用于初始化具有NULL指针常量的指针.它可以用于初始化类型的对象nullptr_t.当然,它可以用来创建一个整数.
但是一旦它作为参数传递,它就不再是一个神奇的编译器构造.它成为具有具体类型的实际C++对象.当涉及模板参数推导时,它得到最明显的类型:int.
一旦它成为一个int,它就会停止为文字0并且表现得与其他任何一个完全相同int.除非它被用在一个constexpr上下文(比如你的int(0))中,编译器可以发现它确实是一个文字0,因此可以采用它的神奇属性.函数参数永远不会constexpr,因此它们不能参与其中.