函数模板不适用于字符串文本

Function template won't work with string literals

本文关键字:文本 字符串 适用于 不适用 函数模板      更新时间:2023-10-16

背景:

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

问题:

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

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

可以在此处找到相同代码的可运行 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 数组不可复制,所以

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

您的重载是正确的,但Filter需要在operator==重载之前重新排序和定义。重载返回Filter<T>依赖于T,所以在这种情况下Filter的定义可以推迟。但是当你返回Filter<std::string>编译器需要预先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;
}

演示

您可以做的是专门针对T被推导出为char[N]的情况进行Filter。 添加

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

将导致Filter<T>{ *this, val, "==" }调用上述专用化,它将使用std::string来存储val

由于您有 C++17 标签,这里有另一种解决此问题的选项: 演绎指南

#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;
}