Ошибка вывода аргумента шаблона Variadic при передаче списков инициализаторов

Bar содержит std::vector из std::pair из std::array из FooValueAdaptor из. FooValueAdaptor неявно преобразует int в bool в FooValue, что не имеет большого смысла в этом надуманном примере, но имеет смысл в моем приложении. Я реализовал удобную функцию Bar::addEntries для одновременного добавления нескольких записей, но вызов ее с более чем двумя аргументами не компилируется с использованием GCC 4.8.0. См. сообщения об ошибках ниже.

#include <array>
#include <utility>
#include <vector>

enum class FooValue {
    A,
    B,
    C
};

class FooValueAdaptor {
public:
    FooValueAdaptor(bool value)
        : m_value(static_cast<FooValue>(value)) {
    }

    FooValueAdaptor(int value)
        : m_value(static_cast<FooValue>(static_cast<bool>(value))) {
    }

    FooValueAdaptor(FooValue value)
        : m_value(value) {
    }

    operator FooValue() {
        return m_value;
    }

    operator bool() {
        return m_value == FooValue::C;
    }

private:
    FooValue m_value;
};

template<std::size_t nFirst, std::size_t nSecond>
class Bar {
public:
    typedef std::array<FooValueAdaptor, nFirst> First;
    typedef std::array<FooValueAdaptor, nSecond> Second;
    typedef std::pair<First, Second> Entry;

    Bar()
        : m_table() {
    }

    void addEntry(First first, Second second) {
        m_table.push_back(std::make_pair(first, second));
    }

    template <typename... Args>
    void addEntries() {
    }

    template <typename... Args>
    void addEntries(First first, Second second, Args... args) {
        addEntry(first, second);
        addEntries(args...);
    }

private:
    std::vector<Entry> m_table;
};

int main(int argc, char **argv) {
    Bar<2, 1> b;

    b.addEntry({ 0, 0 }, { 0 });
    b.addEntries(
        { 0, 1 }, { 0 },
        { 1, 0 }, { 0 },
        { 1, 1 }, { 1 }
    );

    return 0;
}

Сообщения об ошибках компилятора:

test.cpp: In function ‘int main(int, char**)’:
test.cpp:74:2: error: no matching function for call to ‘Bar<2ul, 1ul>::addEntries(<brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>)’
  );
  ^
test.cpp:74:2: note: candidates are:
test.cpp:53:7: note: template<class ... Args> void Bar<nFirst, nSecond>::addEntries() [with Args = {Args ...}; long unsigned int nFirst = 2ul; long unsigned int nSecond = 1ul]
  void addEntries() {
       ^
test.cpp:53:7: note:   template argument deduction/substitution failed:
test.cpp:74:2: note:   candidate expects 0 arguments, 6 provided
  );
  ^
test.cpp:57:7: note: void Bar<nFirst, nSecond>::addEntries(Bar<nFirst, nSecond>::First, Bar<nFirst, nSecond>::Second, Args ...) [with Args = {}; long unsigned int nFirst = 2ul; long unsigned int nSecond = 1ul; Bar<nFirst, nSecond>::First = std::array<FooValueAdaptor, 2ul>; Bar<nFirst, nSecond>::Second = std::array<FooValueAdaptor, 1ul>]
  void addEntries(First first, Second second, Args... args) {
       ^
test.cpp:57:7: note:   candidate expects 2 arguments, 6 provided

Как я могу помочь дедукции компилятора?


person user2232439    schedule 01.04.2013    source источник
comment
Почему addEntries() является шаблоном?   -  person n. 1.8e9-where's-my-share m.    schedule 01.04.2013
comment
При передаче braced-init-list не происходит вывода аргумента шаблона, поскольку такой braced-init-list на самом деле не является выражением.   -  person Xeo    schedule 01.04.2013
comment
@Xeo Спасибо, что указали на это. Эта ссылка объясняет это очень подробно: en.cppreference.com/w/cpp/language/ list_initialization   -  person user2232439    schedule 01.04.2013


Ответы (1)


Вам нужно явно указать компилятору, что вам нужно:

void addEntries(std::initializer_list<std::pair<First, Second>> il) {
   for( const auto& e : il ) {
      addEntry(e.first,e.second);
   }
}

и назовите это так:

b.addEntry({{ 0, 0 }}, {{ 0 }});
b.addEntries({
    {{{ 0, 1 }}, {{ 0 }}},
    {{{ 1, 0 }}, {{ 0 }}},
    {{{ 1, 1 }}, {{ 1 }}}
});

Обратите внимание на огромное количество фигурных скобок, но я думаю, что на самом деле это единственный правильный синтаксис. И GCC 4.8, и Clang 3.2 принимают меньше квадратных скобок, но Clang выдает много предупреждений, приведенное выше исправляет это. Некоторые люди уже работают над "исправлением", но это займет некоторое время.

person Daniel Frey    schedule 01.04.2013
comment
Мне так не терпелось попробовать вариативные шаблоны, что мне и в голову не пришло использовать initializer_list из pair. Говорите о том, что не видите леса за деревьями. ;) Спасибо. - person user2232439; 01.04.2013