Почему С++ не может вывести тип шаблона из назначения?

int x = fromString("test"): не удалось вывести аргумент шаблона для 'ValueType'

int x = fromString<int>("test"): работает нормально, как ожидалось

Итак, почему компилятор здесь работает? Я вижу это со всеми видами реальных функций шаблона, а не только этим глупым примером. Это должна быть особенность языка, но что?

Ответы

Ответ 1

С++ не делает вывод типа для возвращаемого значения. I.e., тот факт, что он присваивается int, не используется в выводе параметра шаблона.

(Удалено редактирование, так как кто-то еще представил перегруженное решение литья уже.)

Ответ 2

Вы не можете выводить на основе возвращаемого типа. Однако вы можете реализовать обходной путь с аналогичным синтаксисом, используя перегруженный оператор литья:

#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

Ответ 3

Похоже, ваш шаблон имеет шаблон типа возвращаемого шаблона, который нельзя автоматически вывести, поэтому вам нужно добавить его здесь.

Ответ 4

Помимо плохого выбора для примера (вероятно, имеет смысл иметь 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

Ответ 5

Тип возврата функции зависит от разрешения перегрузки, а не наоборот.

Существует трюк, который работает: 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; }