转换运算符模板专业化 [英] Conversion operator template specialization

查看:104
本文介绍了转换运算符模板专业化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在很大程度上,这是一个了解转换运算符,模板和模板专业知识的学术活动.以下代码中的转换运算符模板可用于intfloatdouble,但与std::string ...结合使用时将失败.我已经创建了对std::string的转换的特殊化,当与初始化std::string s = a;一起使用时可以使用,但是与强制转换static_cast<std::string>(a)一起使用时可以失败.

Here's a largely academic exercise in understanding conversion operators, templates and template specializations. The conversion operator template in the following code works for int, float, and double, but fails when used with std::string... sort of. I've created a specialization of the conversion to std::string, which works when used with initialization std::string s = a;, but fails when used with a cast 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
}

上面的代码在g ++和icc中生成一个编译器错误,都抱怨没有用户定义的转换适合将MyClass实例转换为static_cast上的std::string(C样式强制转换的行为相同). ).

The above code generates a compiler error in g++ and icc, both complaining that no user-defined conversion is suitable for converting a MyClass instance to a std::string on the static_cast (C-style casts behave the same).

如果我将上面的代码替换为转换运算符的显式非模板版本,一切都会很高兴:

If I replace the above code with explicit, non-template versions of the conversion operator, everything is happy:

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();
    }
};

我的std::string模板专业化怎么了?为什么它对初始化有效,但不能强制转换?

What is wrong with my template specialization for std::string? Why does it work for initialization but not casting?

更新:

在@ luc-danton进行了一些模板向导之后(我从未见过的元编程技巧),启用了实验性的C ++ 0x扩展后,我在g ++ 4.4.5中使用了以下代码.除了对这里所做的事情感到恐惧之外,仅需要实验性的编译器选项就足以使我们这样做.无论如何,希望这对其他人和我一样具有教育意义:

After some template wizardry by @luc-danton (meta-programming tricks I'd never seen before), I have the following code working in g++ 4.4.5 after enabling experimental C++0x extensions. Aside from the horror of what is being done here, requiring experimental compiler options is reason enough alone to not do this. Regardless, this is hopefully as educational for others as it was for me:

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; }
};

这显然迫使编译器为std::string选择转换operator std::string(),这克服了编译器遇到的任何歧义.

This apparently forces the compiler to choose the conversion operator std::string() for std::string, which gets past whatever ambiguity the compiler was encountering.

推荐答案

您只需使用

std::string t(a);

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

Combined with the actual error from GCC (error: call of overloaded 'basic_string(MyClass&)' is ambiguous) we have strong clues as to what may be happening: there is one preferred conversion sequence in the case of copy initialization (std::string s = a;), and in the case of direct initialization (std::string t(a); and static_cast) there are at least two sequences where one of them can't be preferred over the other.

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

Looking at all the std::basic_string explicit constructors taking one argument (the only ones that would be considered during direct initialization but not copy initialization), we find explicit basic_string(const Allocator& a = Allocator()); which is in fact the only explicit constructor.

不幸的是,除了诊断之外,我无法做更多的事情:我想不出一个发现是否实例化operator std::allocator<char>的窍门(我尝试了SFINAE和operator std::allocator<char>() = delete;,但没有成功),我也知道关于功能模板的专业化,重载解析和库要求的了解很少,以了解GCC的行为是否符合要求.

Unfortunately I can't do much beyond that diagnostic: I can't think of a trick to discover is operator std::allocator<char> is instantiated or not (I tried SFINAE and operator std::allocator<char>() = delete;, to no success), and I know too little about function template specializations, overload resolution and library requirements to know if the behaviour of GCC is conforming or not.

由于您说的是学术性练习,所以我将避免使用通常的方法,即非显式转换运算符不是一个好主意.我认为您的代码足以说明为什么:)

Since you say the exercise is academic, I will spare you the usual diatribe how non-explicit conversion operators are not a good idea. I think your code is a good enough example as to why anyway :)

我让SFINAE上班了.如果运算符声明为:

I got SFINAE to work. If the operator is declared as:

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();

然后将没有歧义,并且代码将被编译,std::string的特殊化将被选择,并且所产生的程序将按预期运行.关于复制初始化为什么很好,我仍然没有任何解释.

Then there is no ambiguity and the code will compile, the specialization for std::string will be picked and the resulting program will behave as desired. I still don't have an explanation as to why copy initialization is fine.

这篇关于转换运算符模板专业化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆