【问题标题】:Templated constructor ambiguity when calling overloaded function with castable type调用具有可转换类型的重载函数时模板化构造函数歧义
【发布时间】:2019-07-02 05:28:48
【问题描述】:

我有 2 个类,两个都有单参数模板化构造函数。一个用于整数类型的全部捕获,而在另一个类中,它用于绑定任何可迭代对象。对于每个类型的特定函数,我有两个重载。如果我用整数类型或字符串调用函数,或者至少对 一个 类有效的函数,我会收到关于调用歧义的错误。

#include <string>

class A {
public:
    template <typename Iterable>
    A(Iterable it) : s(it.begin(), it.end()) {} 
private:
    std::string s;
};

class B {
public:
    template <typename Integer>
    B(Integer i) : i(i + 1) {}
private:
    int i;
};

void Use(A a)
{
   // some thing
}

void Use(B b)
{
    // some other thing
}

int main(void)
{
    Use(0);
    return 0;
}

编译器似乎对多态性集的研究不够深入,无法确定确实只有一种可能的解决方案。这可能是因为模板在函数重载之前被“解析”了吗?如何给编译器一些帮助?

【问题讨论】:

  • 模板参数的名称只是一个名称。命名 Iteratable 对编译器没有任何意义。为什么不向A 的构造函数提供两个参数,即分别给出beginend?许多标准容器都是这样做的。 (另外:支持子范围的容器也会变得更加灵活。)
  • 你也可以考虑std::begin()std::end()。这支持所有带有begin()end() 的容器,但不限于其他内容,例如纯 C 数组。
  • “编译器”不会查看函数的主体来确定它是否可以被调用。这也是一件好事,因为这样的事情会使 C++ 中的软件开发变得相当混乱。
  • C++20 的概念可能会有所帮助,目前,SFINAE 可能会有所帮助。
  • 因为您在函数模板实现中犯的任何小错误默默地 使其无效并选择不同的重载。现在就去找你的错误。

标签: c++ templates constructor overloading overload-resolution


【解决方案1】:

编译器似乎没有深入研究这组多态性以确定确实只有一种可能的解决方案。

注意overload resolution是基于函数模板的签名进行的,包括函数名、函数参数、模板参数等;但不是实现(例如函数体),在overload resolution 期间不会检查。

您可以通过添加另一个具有默认值的模板参数来应用SFINAE 来限制构造函数模板可以接受的类型。例如

template <typename Iterable, typename = std::void_t<decltype(std::declval<Iterable>().begin()),
                                                    decltype(std::declval<Iterable>().end())>>
A(Iterable it) : s(it.begin(), it.end()) {} 

template <typename Integer, typename = std::void_t<decltype(std::declval<Integer>() + 1)>>
B(Integer i) : i(i + 1) {} 

LIVE

【讨论】:

    【解决方案2】:

    编译器在遵循 SFINAE 规则时不考虑方法的实现。换句话说,它看到了接受单个参数的 A 类构造器的声明。

    如果您希望 SFINAE 消除此选择,则需要将替换失败的表达式移至函数签名。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-28
      • 1970-01-01
      相关资源
      最近更新 更多