Ответ 1
С++ не делает вывод типа для возвращаемого значения. I.e., тот факт, что он присваивается int, не используется в выводе параметра шаблона.
(Удалено редактирование, так как кто-то еще представил перегруженное решение литья уже.)
int x = fromString("test")
: не удалось вывести аргумент шаблона для 'ValueType'
int x = fromString<int>("test")
: работает нормально, как ожидалось
Итак, почему компилятор здесь работает? Я вижу это со всеми видами реальных функций шаблона, а не только этим глупым примером. Это должна быть особенность языка, но что?
С++ не делает вывод типа для возвращаемого значения. I.e., тот факт, что он присваивается int, не используется в выводе параметра шаблона.
(Удалено редактирование, так как кто-то еще представил перегруженное решение литья уже.)
Вы не можете выводить на основе возвращаемого типа. Однако вы можете реализовать обходной путь с аналогичным синтаксисом, используя перегруженный оператор литья:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
class FromString{
private:
string m_data;
public:
FromString(const char*data) : m_data(data) {}
template<typename T>
operator T(){
T t;
stringstream ss(m_data);
ss >> t;
return t;
}
};
template<> FromString::operator bool(){
return (m_data!="false"); //stupid example
}
int main(){
int ans = FromString("42");
bool t = FromString("true");
bool f = FromString("false");
cout << ans << " " << t << " " << f << endl;
return 0;
}
Вывод:
42 1 0
Похоже, ваш шаблон имеет шаблон типа возвращаемого шаблона, который нельзя автоматически вывести, поэтому вам нужно добавить его здесь.
Помимо плохого выбора для примера (вероятно, имеет смысл иметь int x = to<int>("1235")
, а не toString
), проблема заключается в том, что тип возвращаемого значения не участвует в разрешении перегрузки или вводе типа [1]. Причиной этого является то, что выражение может использоваться во многих местах, где тип возврата не может быть выведен:
// assuming template <typename T> T to( std::string ):
//
f( to("123") ); // where there are two overloads f(int), f(double)
int x = 1.5 * to("123"); // T == int? T == double?
to("123"); // now what? returned object can be ignored!
Таким образом, решение состоит в том, что тип возврата не будет принимать участия в разрешении перегрузки или выводе типа.
[1] Существует одно исключение из этого правила, которое представляет собой оценку указателя функции с более чем одной перегрузкой, где перегрузка должна быть выбрана либо указателем назначения, либо явным cast, но это только одно исключение и не используется ни в каком другом контексте:
void f();
void f(int);
void g( void (*)() );
void g( void (*)(int) );
void (*p1)() = &f; // overload selected based on destination type
void (*p2)(int) = &f;
g( (void (*)(int))&f ); // overload selected based on explicit cast
Тип возврата функции зависит от разрешения перегрузки, а не наоборот.
Существует трюк, который работает: operator=
обычно существует только для равных типов аргументов LHS/RHS, за исключением случаев, когда определен явный operator=
(независимо от того, является ли он автономным или как член).
Таким образом, разрешение перегрузки найдет operator=(int &, int)
и посмотрит, может ли возвращаемое значение из вашей функции конвертироваться в int. Если вы возвращаете временное значение, имеющее operator int
, это приемлемое разрешение (даже если operator int
находится в общей форме template<typename T> operator T
).
Таким образом:
template<typename T, typename U>
U convert_impl(T const &t);
template<typename T>
struct convert_result {
convert_result(T const &t) : t(t) { }
template<typename U> operator U(void) const { return convert_impl<U>(t); }
T const &t;
};
template<typename T>
convert_result<T> convert(T const &t) { return t; }