使用可转换类型调用重载函数时模板构造函数歧义

Templated constructor ambiguity when calling overloaded function with castable type

我有 2 个 class 都有单参数模板化构造函数。一个是整数类型的全部,另一个 class 它用于绑定任何可迭代对象。我有两个针对特定函数的重载,这些函数将使用这些类型中的每一种。如果我使用整数类型或字符串或至少适用于 class 中的 one 的类型调用函数,我会收到有关调用歧义的错误消息。

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

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

The compiler doesn't seem to be looking far enough into the set of polymorphisms to determine that there really only is one possible solution.

注意overload resolution is performed based on the signature of function templates, including function names, function parameters, template parameters, etc; but not implementations (e.g. the function body), which won't be examined during 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

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

如果希望SFINAE去掉这个选择,需要将替换失败的表达式移动到函数签名中。