【问题标题】:Is there a way to make the compiler pick a certain overload where its somewhat ambiguous?有没有办法让编译器在它有点模棱两可的地方选择某个重载?
【发布时间】:2017-11-13 09:14:15
【问题描述】:

我知道了:

json& add(char* key, char* value) {
    s.append("\"").append(key).append("\":\"").append(value).append("\",");
    return *this;
}

json& add(char* key, const char* value) {
    s.append("\"").append(key).append("\":\"").append(value).append("\",");
    return *this;
}

template <typename vt>
json& add(char* key, vt value) {
    s.append("\"").append(key).append("\":").append(std::to_string(value)).append(",");
    return *this;
}

所以这个函数有 3 个重载。

第一个应该处理一个 char* 值。

最后一个应该处理所有的数字类型,如 uint_8t、int_8t、uint16_t ....float、double

如果我使用这个功能,例如:

void test(json obj, char* value) {
    obj.add("test", value);
}

然后一切正常,只有第一个和最后一个重载。但是,如果我尝试这样做:

void test(json obj) {
    obj.add("test", "blah");
}

然后它将尝试使用最后一个重载(带有模板的那个)而不是 char* 一个,因为从技术上讲,我正在将一个 const char* 传递给它。

所以现在我需要为此设置中间重载。

问题是 - 有没有办法为它应该处理的模板定义一系列类型?将 vt 限制为我提到的数字类型?那应该将编译器转移到使用 char* 重载,而我不需要 const char* 一个。

【问题讨论】:

  • 你的意思是你不需要char*one,对吧?
  • @KarstenKoop 要么对我有用
  • 将一个或多个参数转换为所需重载所需的类型。注意没有“有点模棱两可”这样的东西:有模棱两可的和非模棱两可的。
  • 添加到模棱两可/非模棱两可的评论,详细信息在这里en.cppreference.com/w/cpp/language/overload_resolution在“最佳可行功能”下。第 3 点说非模板优于模板,但在这种情况下,如果您没有 const char* 重载,则将模板作为 const char* 转换为 char 不是可行的转换。

标签: c++ templates


【解决方案1】:

不需要非常量 char* 重载,因为您不修改参数。 所以剩下的就是你超载的第二个也是最后一个版本。

您说模板化重载仅适用于整数或浮点数类型。我建议使用std::enable_if_t&lt;&gt; 来限制对这些类型的访问。

我还建议您将密钥设为const char*

#include <string>

class json
{
private:
    std::string s;

public:
    json& add(const char* key, const char* value) {
        s.append("\"").append(key).append("\":\"").append(value).append("\",");
        return *this;
    }

    template <typename vt, typename = std::enable_if_t<std::is_arithmetic_v<vt>>>
    json& add(const char* key, vt value) {
        s.append("\"").append(key).append("\":").append(std::to_string(value)).append(",");
        return *this;
    }
};

int main(int, char**)
{
    json j; double d = 0.1245;
    j.add("Hello1", "World");
    j.add("Hello2", 1234);
    j.add("Hello3", 1234.1f);
    j.add("Hello4", d);
}

【讨论】:

  • 这是这个特定测试用例的解决方案,但不是问题。我还想对 std::string 使用相同的重载,并将键类型模板化,这样我就不必编写 15 个重载,每个函数只需要 3 个。
  • "将 vt 限制为我提到的数字类型?"我的例子只是说明了这一点。
  • 不幸的是,该部分似乎无法以对我有帮助的方式工作,MSVC 只是说 x 重载具有类似的转换,并且 const char* 和 char* 之间的转换仅适用于值类型如果模板重载被移除,则隐含地。
  • 那么也许您应该重新表述您的问题,以便它反映您想知道的内容。我的回答准确地显示了如何按照您的要求限制模板,甚至表明它有效:godbolt.org/g/acXon8
  • 注意:我只是 d 双值的文字。 gcc 可以处理这个,但 MSVC 不能。只需将其删除。
【解决方案2】:

今天的方法是使用 enable_if 和类型特征。您想按如下方式定义模板函数:

    template <typename vt, typename = std::enable_if_t<std::is_arithmetic_v<vt>>>
json& add(const char* key, vt value) {
    s.append("\"").append(key).append("\":").append(std::to_string(value)).append(",");
    return *this;
}

注意这部分:typename = std::enable_if_t&lt;std::is_arithmetic_v&lt;vt&gt;&gt;&gt;

希望在下一个标准中,我们将获得能够简化这种奇怪语法的概念。

如果您正在寻找更多信息以及该技术背后的背景,这是一篇非常好的文章:https://eli.thegreenplace.net/2014/sfinae-and-enable_if/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-03
    • 1970-01-01
    • 2015-07-04
    • 2016-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多