转换运算符模板专业化

Nat*_*han 20 c++ templates metaprogramming template-specialization

这是理解转换运算符,模板和模板特化的主要学术练习.在下面的代码转换操作符模板工程int,float以及double,但使用时失败std::string...之类的.我已经创建了转换为的特化std::string,它在初始化时使用std::string s = a;,但在与强制转换一起使用时失败static_cast<std::string>(a).

#include <iostream>
#include <string>
#include <sstream>

class MyClass {
     int y;
public:
    MyClass(int v) : y(v) {}
    template <typename T>
    operator T() { return y; };
};

template<>
MyClass::operator std::string() {
    std::stringstream ss;
    ss << y << " bottles of beer.";
    return ss.str();
}

int main () {
    MyClass a(99);
    int i    = a;
    float f  = a;
    double d = a;
    std::string s = a;

    std::cerr << static_cast<int>(a) << std::endl;
    std::cerr << static_cast<float>(a) << std::endl;
    std::cerr << static_cast<double>(a) << std::endl;
    std::cerr << static_cast<std::string>(a) << std::endl; // Compiler error
}
Run Code Online (Sandbox Code Playgroud)

上面的代码生成克一个编译错误++和ICC,既抱怨没有用户定义的转换是适合于转换MyClass实例与std::stringstatic_cast(C-风格转换行为相同).

如果我用转换运算符的显式非模板版本替换上面的代码,一切都很开心:

class MyClass {
    int y;
public:
    MyClass(int v) : y(v) {}
    operator double() {return y;}
    operator float()  {return y;}
    operator int()    {return y;}
    operator std::string() {
        std::stringstream ss;
        ss << y << " bottles of beer.";
        return ss.str();
    }
};
Run Code Online (Sandbox Code Playgroud)

我的模板专业化有std::string什么问题?为什么它适用于初始化但不能进行转换?

更新:

经过@ luc-danton的一些模板魔法(我之前从未见过的元编程技巧),在启用实验性C++ 0x扩展后,我在g ++ 4.4.5中使用了以下代码.除了这里正在做的事情的恐怖之外,要求实验性的编译器选项是足够的理由不能做到这一点.无论如何,这对我来说是有教育意义的:

class MyClass {
    int y;
public:
    MyClass(int v) : y(v) {}

    operator std::string() { return "nobody"; }

    template <
        typename T
        , typename Decayed = typename std::decay<T>::type
        , typename NotUsed = typename std::enable_if<
            !std::is_same<const char*, Decayed>::value &&
            !std::is_same<std::allocator<char>, Decayed>::value &&
            !std::is_same<std::initializer_list<char>, Decayed>::value
          >::type
    >
    operator T() { return y; }
};
Run Code Online (Sandbox Code Playgroud)

这显然是强制编译器选择转换operator std::string()std::string,它得到过去任何歧义的编译器遇到.

Luc*_*ton 9

您只需使用即可重现该问题

std::string t(a);
Run Code Online (Sandbox Code Playgroud)

结合GCC(error: call of overloaded 'basic_string(MyClass&)' is ambiguous)的实际错误,我们可以找到有关可能发生的事情的强有力线索:在复制初始化(std::string s = a;)的情况下有一个首选的转换序列,在直接初始化(std::string t(a);static_cast)的情况下,至少有两个其中一个不能优先于另一个的序列.

查看所有std::basic_string带有一个参数的显式构造函数(在直接初始化期间只考虑但不是复制初始化的那些),我们发现explicit basic_string(const Allocator& a = Allocator());哪个实际上是唯一的显式构造函数.

不幸的是,除了那个诊断之外,我做不了多少:我想不出一个发现的技巧operator std::allocator<char>是实例化还是没有(我试过SFINAE operator std::allocator<char>() = delete;,但没有成功),而且我对功能模板特化,重载分辨率和图书馆要求知道GCC的行为是否符合要求.

既然你说这个练习是学术性的,我会饶恕你们常用的dia骂,非显性转换操作符不是一个好主意.我认为你的代码是一个很好的例子,为什么反正:)


我让SFINAE工作了.如果运算符声明为:

template <
    typename T
    , typename Decayed = typename std::decay<T>::type
    , typename = typename std::enable_if<
        !std::is_same<
            const char*
            , Decayed
        >::value
        && !std::is_same<
            std::allocator<char>
            , Decayed
        >::value
        && !std::is_same<
            std::initializer_list<char>
            , Decayed
        >::value
    >::type
>
operator T();
Run Code Online (Sandbox Code Playgroud)

然后没有歧义,代码将编译,std::string将选择特化,结果程序将按照需要运行.我仍然没有解释为什么复制初始化很好.