代码之家  ›  专栏  ›  技术社区  ›  P.W

以下变量模板行为是否不一致?

  •  3
  • P.W  · 技术社区  · 6 年前

    我试图通过下面的例子来理解变量模板,发现其行为有些不一致。

    #include <iostream>
    #include <string>
    using namespace std;
    
    template<typename T>
    T adder(T v) {  
      return v;
    }
    
    template<typename T, typename... Args>
    T adder(T first, Args... args) {    
      return first + adder(args...);
    }
    
    int main()
    {   
        long sum = adder(1, 2, 3, 8, 7); //Works
        cout << sum << endl;
    
        string s1 = "xx", s2 = "aa", s3 = "bb", s4 = "yy";
    
        string ssum = adder(s1, s2, s3, s4); //Works
        cout << ssum << endl;
    
        string ssum2 = s1 + s2 + "3" + "4"; //Works as operator '+' is defined in string class for const char*
        cout << ssum2 << endl;
    
        string ssum3 = adder(s1, s2, "3", "4"); //Does not work. Expected as  binary 'operator+' is not defined for string and const char*
        cout << ssum3 << endl;
    
        string ssum4 = adder("3", "4"); //Does not work. Expected as  binary 'operator+' is not defined for const char* and const char*
        cout << ssum4 << endl;
    
        string ssum5 = adder(s1, s2, "3"); //Works!!! 
        cout << ssum5 << endl;  
    
    }   
    

    打电话 adder 对于 ssum3 ssum4 失败,但适用于 ssum5 . 这种行为是否一致?这是因为 SSM5 转换为 string 在最后的迭代中?

    3 回复  |  直到 6 年前
        1
  •  2
  •   Michael Veksler    6 年前

    你可以用 std::common_type .

    正如其他人所指出的,这与递归的顺序有关。

    adder(s1, s2, "3")
    

    与以下内容相同:

    s1 + adder(s2, "3")
    

    因为这和 std::strings std::string 用一个 const char* ,这是合法的):

    s1 + (s2 + ("3")))
    

    另一方面

    adder(s1, s2, "3", "4");
    

    显然是不行的,因为它是相同的 常量字符* ,这是非法的):

    s1 + (s2 + ("3" + ("4")))
    

    为了克服这个问题,你应该使用 STD:普通型 ,将执行公共类型(即 STD::字符串 在这种情况下):

    template<typename T>
    T adder(const T & v) {  
      return v;
    }
    
    template<typename T, typename... Args>
    T adder(const T & first, const Args &... args) {    
      return first + adder<typename std::common_type<T, Args...>::type>(args...);
    }
    

    另一种选择是使用C++ 17个表达式(如果您有C++ 17):

    template <typename ... Args>
    typename std::common_type<Args...>::type
    adder2(const Args & ... args)
    {
        using type = typename std::common_type<Args...>::type;
        return (type(args) + ... );
    }
    

    缺点是它会导致诸如字符串之类的类型的复制构造函数,并且它需要C++ 17。通过使用helper函数,可以消除额外的构造(包括公共基类的构造):

    template <typename Target, typename Source>
    typename std::enable_if< ! std::is_base_of<Target, Source>::value, 
                             Target>::type 
    toType(const Source & source)
    {
        return source;
    }
    template <typename Target, typename Source>
    typename std::enable_if<std::is_base_of<Target, Source>::value, 
                            const Target&>::type
    toType(const Source & source)
    {
        return source;
    }
    template <typename ... Args>
    typename std::common_type<Args...>::type
    addder3(const Args & ... args)
    {
        using type = typename std::common_type<Args...>::type;
        return (toType<type>(args) + ... );
    }
    
        2
  •  4
  •   songyuanyao    6 年前

    这是因为 ssum5 转换为 string 在最后的迭代中?

    不,它起作用是因为 std::operator+(std::basic_string) 超负荷接受 std::string 和原始字符串( const char* )鉴于 s2 是一个 STD::字符串 ,然后两者都是 s2 + "3" "3" + s2 干得好。所以 SSM5 有效,因为最后递归它将被解释为 S2+“3” 很好。

    问题是,不能将两个连续的原始字符串传递给 adder . 为了 ssum3 ssum4 你路过了 "3" "4" 最后他们会被解释为 "3" + "4" 这显然行不通。

        3
  •  1
  •   James Picone    6 年前

    SSUM3是 adder(s1, adder(s2, adder("3", adder("4"))))

    SSUM5是 adder(s1, adder(s2, adder("3")))

    第一个案例包含实例化 adder("3", adder("4")) 扩展到 "3" + "4" 最终,这显然不起作用。

    第二个案例从来没有试图增加两个 const char* 在一起,所以很好。