Неопределенность перегрузки C ++ std :: vector initializer_list (g ++ / clang ++) - PullRequest
10 голосов
/ 11 марта 2019

Рассмотрим следующий код:

#include <vector>

#define BROKEN

class Var {
public:
#ifdef BROKEN
    template <typename T>
    Var(T x) : value(x) {}
#else
    Var(int x) : value(x) {}
#endif

    int value;
};

class Class {
public:
    Class(std::vector<Var> arg) : v{arg} {}

    std::vector<Var> v;
};

Clang ++ (7.0.1) компилирует это без ошибок, независимо от того, определено BROKEN или нет, но g ++ (8.2.1) выдает ошибку, если BROKENопределяется:

main.cpp:9:20: error: cannot convert ‘std::vector<Var>’ to ‘int’ in initialization
  Var(T x) : value(x) {}
                    ^

Насколько я знаю, используемая здесь унифицированная инициализация должна выбирать конструктор std::vector(std::vector&&) в обоих случаях;однако, очевидно, что g++ вместо этого видит {arg} в качестве списка инициализатора и пытается инициализировать первый элемент v с Var, примененным к вектору, что не будет работать.

ЕслиBROKEN не определено, g ++, по-видимому, достаточно умен, чтобы понять, что перегрузка initializer_list не будет работать.

Какое поведение является правильным, или оба допускаются стандартом?

BROKEN определен gcc
BROKEN не определен gcc
BROKEN определен лязг

1 Ответ

2 голосов
/ 11 марта 2019

Вот два способа решения этой проблемы:

class Var {
public:
#ifdef BROKEN
    template <typename T>
    Var(T x, typename std::enable_if<std::is_convertible<T, int>::value>::type* = nullptr) : value(x) {}
#else
    Var(int x) : value(x) {}
#endif

    int value;
};

Демоверсия .

Или:

class Class {
public:
    Class(std::vector<Var> arg) : v(arg) {}

    std::vector<Var> v;
};

Live demo .

Теперь, по-видимому, в случае gcc пытается использовать параметр шаблона в качестве списка инициализации.Когда используются фигурные скобки и определен список инициализации, список инициализации имеет более высокий приоритет, чем конструктор копирования.

Таким образом, добавление правильного enable_if решает проблему, поскольку защищает от создания версии конструктора из списка инициализации.В C ++ 20 концепции решат эту проблему лучше.

И когда вместо скобок для инициализации используется v, список инициализации не является предпочтительным.

Я не уверен, ктоверно (IMO лязг, но это просто внутреннее чувство).Может быть, кто-то, кто знает стандарт лучше, может сказать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...