Специализация шаблона оператора преобразования

Вот в основном академическое упражнение по пониманию операторов преобразования, шаблонов и специализаций шаблонов. Шаблон оператора преобразования в следующем коде работает для int , float и double , но не работает при использовании с std :: string ... вроде, как бы, что-то вроде. Я создал специализацию преобразования в std :: string , которая работает при использовании с инициализацией std :: string s = a; , но не работает при использовании с приведением static_cast (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 в std :: string на static_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();
    }
};

Что не так с моей специализацией шаблона для std :: string ? Почему он работает для инициализации, но не для преобразования?

Обновление:

После некоторых волшебных действий с шаблонами от @ luc-danton (трюки метапрограммирования, которые я никогда раньше не видел), у меня есть следующий код, работающий в g ++ 4.4. 5 после включения экспериментальных расширений C ++ 0x.Помимо ужаса того, что здесь делается, необходимость экспериментальных опций компилятора - уже достаточная причина, чтобы не этого делать. Тем не менее, мы надеемся, что это будет таким же образовательным для других, как и для меня:

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 () для std :: string , что позволяет избежать неоднозначности, с которой столкнулся компилятор.

20
задан Nathan 12 October 2011 в 16:49
поделиться