Pet*_*ham 5 c++ casting implicit-conversion variadic-templates c++11
鉴于此代码:
#include <iostream>
template<typename... Args> class X;
template<typename T>
class X<T> {
public:
T value;
X(T value_) : value(value_) {}
};
template<typename T, typename... Args>
class X<T, Args...> : public X<Args...> {
public:
T value;
X(T value_, Args... args) : value(value_), X<Args...>(args...) {}
};
template<typename T>
std::ostream& operator <<(std::ostream& stream, const X<T>& value_) {
stream << value_.value;
return stream;
}
template<typename T, typename... Args>
std::ostream& operator <<(std::ostream& stream, const X<T, Args...>& value_) {
stream << value_.value << " " << static_cast<X<Args...> const& >(value_);
return stream;
}
class Person : public X<std::string, int>{
public:
Person(std::string name, int age) : X<std::string, int>(name, age) {}
};
int main()
{
std::cout << Person("Me", 35) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用'g ++ -std = c ++ 11 main.cpp'用gcc 4.7.2编译,执行时给出以下输出:
35
Run Code Online (Sandbox Code Playgroud)
我期待它打印:
Me 35
Run Code Online (Sandbox Code Playgroud)
因为它应该首先匹配运算符<<对于Person的直接超类而不是它的超级超类,对吧?但这似乎并没有发生.它只打印了int,这是X <int>的行为,而不是两个项目,它们是X <std :: string,int>的行为,直接超类.这是预期的吗?
发布的代码有两个问题。首先,以下之间的歧义:
template<typename T>
std::ostream& operator <<(std::ostream& stream, const X<T>& value_) {
Run Code Online (Sandbox Code Playgroud)
和:
template<typename T, typename... Args>
std::ostream& operator <<(std::ostream& stream, const X<T, Args...>& value_) {
Run Code Online (Sandbox Code Playgroud)
在参数包为空的情况下。可以改为使用这两个函数来解决此问题:
template<typename T>
std::ostream& operator <<(std::ostream& stream, const X<T>& value_) {
Run Code Online (Sandbox Code Playgroud)
和:
template<typename T, typename T2, typename... Args>
std::ostream& operator <<(std::ostream& stream, const X<T, T2, Args...>& value_) {
Run Code Online (Sandbox Code Playgroud)
现在模板匹配的歧义已经解决。
这就给我们留下了第二个问题。这行:
std::cout << Person("Me", 35) << std::endl;
Run Code Online (Sandbox Code Playgroud)
Person 没有提供operator<< 的实现,因此编译器只能转换为具有实现的类型,在本例中,X< std::string, int >和X< int >都有这样的实现。然而,编译器现在可以在这两者之间进行选择,因为它们都是 Person 的超类。
这可以通过两种方式解决,首先为 Person 添加特定的运算符<<:
std::ostream& operator <<(std::ostream& stream, const Person &value_) {
stream << static_cast<const X<std::string, int, int>&>(value_);
return stream;
}
Run Code Online (Sandbox Code Playgroud)
效果很好,但我发现过于冗长。或者,我们可以通过删除类层次结构并仅使用成员变量来包含“其他”值来消除歧义:
template<typename T>
class X<T> {
public:
T value;
X(T value_) : value(value_) {}
};
template<typename T>
std::ostream& operator <<(std::ostream& stream, const X<T>& value_) {
stream << value_.value << ".";
return stream;
}
// Empty arg packs will match ambiguously with nothing so make sure the empty pack case is distanct from the base case
template<typename T, typename T2, typename... Args>
class X<T, T2, Args...> {
public:
T value;
X<T2, Args...> superValue;
X(T value_, T2 arg, Args... args) : value(value_), superValue(arg, args...) {}
};
template<typename T, typename T2, typename... Args>
std::ostream& operator <<(std::ostream& stream, const X<T, T2, Args...>& value_) {
stream << value_.value << "," << value_.superValue;
return stream;
}
Run Code Online (Sandbox Code Playgroud)
现在 Person 只能隐式地转换为X < std::string,int >其他任何东西,并且不存在歧义。最近的 g++ 和 clang 都可以毫无问题地编译它。
至于在模板解析中是否没有任何内容应该与空参数包匹配,或者是否应该在转换解析中匹配更直接的基类以支持不太直接的基类,我留给比我更熟悉 C++ 标准的人。