Это академическое упражнение по пониманию операторов преобразования, шаблонов и шаблонных специализаций.Шаблон оператора преобразования в следующем коде работает для 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
}
Приведенный выше код генерирует компиляторошибка в 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; }
};
Это, очевидно, заставляет компилятор выбирать преобразование operator std::string()
для std::string
, которое преодолевает любую неопределенность, с которой сталкивался компилятор.