【问题标题】:Function template won't work with string literals函数模板不适用于字符串文字
【发布时间】:2019-07-15 16:19:09
【问题描述】:

背景:

我正在开发一个查询 DSL,它将解析带有 ==< 等的表达式,并通过运算符重载返回过滤器对象。

问题:

与字符串文字一起使用时,我的模板方法失败。我尝试提供模板的特定实例化同时采用std::stringchar,但似乎都不起作用。

代码如下。在 main 中导致问题的行标有注释。我尝试过的替代解决方案在代码中被注释掉。

可以在here找到相同代码的可运行repl。

我确实知道用std::string("text") 手动包装字符串文字会起作用,但如果可能的话,我希望能够使用纯字符串文字。

#include <iostream>

template<typename T>
struct Filter;

struct Field
{
    Field(const std::string &val): name(val) { }
    Field(std::string &&val): name(std::move(val)) { }
    std::string name;

    // template <signed N>
    // Filter<std::string> operator==(const char (&val) [N]);

    template <typename T>
    Filter<T> operator==(const T &val);
};

template <typename T>
Filter<T> Field::operator==(const T &val)
{
    return Filter<T>{ *this, val, "==" };
}

// template <signed N>
// Filter<std::string> Field::operator==(const char (&val) [N])
// {
//   return Filter<std::string>{ *this, std::string(val), "==" };
// }

// template <>
// Filter<std::string> Field::operator==<std::string>(const std::string &val)
// {
//     return Filter<std::string>{ *this, val, "==" };
// }


template<typename T>
struct Filter
{
    Field f;
    T val;
    std::string op;
};

int main() {
  Field f1 { "field1" };
  Field f2 { "field1" };
  std::cout << (f1 == 1).val;
  std::cout << (f1 == "Hello").val;  // <--- the source of my problems
}

【问题讨论】:

    标签: c++ c++11 c++14 c++17


    【解决方案1】:

    问题是 c-array 是不可复制的,所以

    Filter<char [6]>{ *this, val, "==" }; // Error
    

    您的重载是正确的,但 Filter 需要在您的 operator== 重载之前重新排序和定义。返回Filter&lt;T&gt; 的重载依赖于T,因此在这种情况下Filter 的定义可以推迟。但是,当您返回 Filter&lt;std::string&gt; 时,编译器需要预先定义 Filter 的实际定义。

    #include <iostream>
    
    template<typename T>
    struct Filter;
    
    struct Field
    {
        Field(const std::string &val): name(val) { }
        Field(std::string &&val): name(std::move(val)) { }
        std::string name;
    
        template <std::size_t N> Filter<std::string> operator==(const char (&val) [N]);
    
        template <typename T>
        Filter<T> operator==(const T &val);
    };
    
    template<typename T>
    struct Filter
    {
        Field f;
        T val;
        std::string op;
    };
    
    template <typename T>
    Filter<T> Field::operator==(const T &val)
    {
        return Filter<T>{ *this, val, "==" };
    }
    
    template <std::size_t N>
    Filter<std::string> Field::operator==(const char (&val) [N])
    {
       return Filter<std::string>{ *this, std::string(val), "==" };
    }
    
    int main() {
      Field f1 { "field1" };
      Field f2 { "field1" };
      std::cout << (f1 == 1).val;
      std::cout << (f1 == "Hello").val;
    }
    
    

    Demo

    【讨论】:

    • +1 哇。似乎...在operator== 之前将过滤器定义向上移动是我需要做的所有事情。我能麻烦你解释一下这是为什么吗?
    • 如果其他人知道为什么需要重新订购,请说出来 - 我很乐意将其编辑为答案然后接受它:-)
    • Filter&lt;T&gt; 依赖于T,因此定义可能会被推迟,而Filter&lt;std::string&gt; 不依赖于N,因此应该知道定义。
    • 非常感谢!当我回到电脑上时,我将编辑答案以包括它。再次感谢!!!
    【解决方案2】:

    T 被推导出为char[N] 时,您可以做的是专门化Filter。添加

    template<std::size_t N>
    struct Filter<char[N]>
    {
        Field f;
        std::string val;
        std::string op;
    };
    

    将导致Filter&lt;T&gt;{ *this, val, "==" } 调用上述特化,并将使用std::string 存储val

    【讨论】:

    • 加 1 - 完美运行。感谢 TON 提供的信息 - 一定会记住这个技巧以备将来使用。
    【解决方案3】:

    既然你有 C++17 标签,这里还有另一个解决这个问题的方法:deduction guides

    #include <iostream>
    
    template<typename T>
    struct Filter;
    
    struct Field
    {
        Field(const std::string &val): name(val) { }
        Field(std::string &&val): name(std::move(val)) { }
        std::string name;
    
        // note the use of auto here
        template <typename T>
        auto operator==(const T &val);
    };
    
    template <typename T>
    auto Field::operator==(const T &val)
    {
        // do not use Filter<T> here, or the deduction guide won't kick in
        return Filter{ *this, val, "==" };
    }
    
    template<typename T>
    struct Filter
    {
        Field f;
        T val;
        std::string op;
    };
    
    // ------- Deduction Guides -----------
    template<typename T>
    Filter(Field, T, std::string) -> Filter<T>;
    
    // will tell the compiler to create a Filter<string> with a c-array argument
    template<std::size_t N>
    Filter(Field, const char(&)[N], std::string) -> Filter<std::string>;
    // ------------------------------------
    
    int main() {
      Field f1 { "field1" };
      Field f2 { "field1" };
      std::cout << (f1 == 1).val;
    
      // creates a Filter<string> instead of trying to 
      // create a Filter<const char(&)[6]> due to the deduction guide
      std::cout << (f1 == "Hello").val;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-13
      • 2020-02-21
      • 1970-01-01
      • 1970-01-01
      • 2021-01-07
      • 2021-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多