Неоднозначность шаблонного конструктора при вызове перегруженной функции с типом castable - PullRequest
4 голосов
/ 02 июля 2019

У меня есть 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;
}

Компилятор, похоже, недостаточно углубляется в набор полиморфизмов, чтобы определить, что на самом деле существует только одно возможное решение. Может ли это быть из-за того, что шаблон «разрешается» до перегрузки функции? Как мне помочь компилятору?

Ответы [ 2 ]

5 голосов
/ 02 июля 2019

Компилятор, похоже, недостаточно углубляется в набор полиморфизмов, чтобы определить, что на самом деле существует только одно возможное решение.

Обратите внимание, что разрешение перегрузки выполняется на основе сигнатуры шаблонов функций, включая имена функций, параметры функций, параметры шаблонов и т. Д .; но не реализации (например, тело функции), которые не будут проверяться при разрешении перегрузки .

Вы можете применить 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) {} 

ЖИТЬ

1 голос
/ 02 июля 2019

Компилятор не учитывает реализацию метода, если следует правилам SFINAE.Другими словами, он видит объявление константатора класса A, который принимает один аргумент.

Если вы хотите, чтобы SFINAE исключил этот выбор, вам нужно переместить выражение, которое не удалось заменить, в сигнатуру функции.

...