【问题标题】:Why can't C++ deduce template type from assignment?为什么 C++ 不能从赋值中推断模板类型?
【发布时间】:2011-12-31 05:27:49
【问题描述】:

int x = fromString("test")无法推断出“ValueType”的模板参数

int x = fromString<int>("test") : 工作正常

那么为什么编译器会在这里挣扎呢?我看到它有各种真正的模板函数,而不仅仅是这个愚蠢的例子。它一定是语言的一个特征,但是什么?

【问题讨论】:

  • 为什么可以转换成int的东西叫“toString”?
  • This question 讨论了类似的问题。
  • 它“只是没有”。请注意,这是语言,而不是编译器——尽管实际上它并没有在语言中指定,部分原因是编译器在一般情况下变得正确。
  • @MarceloCantos 我的错误,已修复。
  • 如果允许,该语言还需要规则来解决可能出现的明显歧义。例如,template <typename T> T doubleit(T t) { return 2*t; }。现在,int i = doubleit(0.5); 是否调用doubleit<int>(匹配i)或doubleit<double>(匹配0.5)?结果是不同的,所以即使语言有解决歧义的规则,任何阅读代码的人都很容易出错。让子表达式的类型和含义仅取决于子表达式本身,而不是周围的表达式,至少很简单。

标签: c++ templates stl


【解决方案1】:

您无法根据返回类型进行推断。但是,您可以使用重载的强制转换运算符实现具有类似语法的解决方法:

#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

【讨论】:

  • 万能转换功能,哎哟!但是由于operator= 不能是friend,我想这是最好的解决方案,+1。
【解决方案2】:

C++ 不对返回值进行类型推断。即,它被分配给 int 的事实并未用于模板参数推导。

(删除编辑,因为其他人已经提出了重载演员解决方案。)

【讨论】:

  • 谢谢。我需要它,这只是那些有点令人讨厌的怪癖之一,我想了解原因。
【解决方案3】:

除了示例的错误选择(使用int x = to&lt;int&gt;("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] 此规则有一个例外,即对具有多个重载的函数指针的求值,其中重载必须由目标指针或显式选择强制转换,但这只是一个例外,不会在任何其他上下文中使用:

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

【讨论】:

    【解决方案4】:

    看起来您的模板具有模板化的返回类型,无法自动推断,这就是您需要在此处添加它的原因。

    【讨论】:

    • 延迟返回类型声明(或其他任何名称)有不同的用途。它允许您根据函数参数的类型声明返回类型。它只是推迟声明返回类型,直到参数在范围内。它无法从调用者的使用情况中推断出结果的类型。
    • 感谢您的评论,我将从我的帖子中删除 :-)
    【解决方案5】:

    函数的返回类型取决于重载决议,而不是相反。

    但有一个技巧有效:operator= 通常只存在于相同的 LHS/RHS 参数类型,除非定义了显式 operator=(无论是作为独立的还是作为成员都无关紧要)。

    因此,重载解析将找到operator=(int &amp;, int),并查看函数的返回值是否可转换为int。如果您返回具有operator int 的临时值,这是可接受的解决方案(即使operator inttemplate&lt;typename T&gt; 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; }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-26
      • 1970-01-01
      相关资源
      最近更新 更多