Как создать идеальный конструктор пересылки для класса с переменным числом кортежей - PullRequest
5 голосов
/ 04 марта 2012

Я пытаюсь создать что-то похожее на кортеж, но я столкнулся с проблемой написания моего конструктора.

Вот код:

#include <tuple>

template <typename... Ts>
struct B {
    template <typename... ArgTypes>
    explicit B(ArgTypes&&... args)
    {
        static_assert(sizeof...(Ts) == sizeof...(ArgTypes),
            "Number of arguments does not match.");
    }
};

struct MyType {
    MyType() = delete;
    MyType(int x, const char* y) {}
};

int main()
{
   B         <int, char>               a{2, 'c'};                      // works
   B         <int, bool, MyType, char> b{2, false, {4, "blub"}, 'c'};  // fails
   std::tuple<int, bool, MyType, char> t{2, false, {4, "blub"}, 'c'};  // works
}

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

GCC-4.7 выдает следующее:

Clang-3.1 следующее:

vararg_constr.cpp:21:40: error: no matching constructor for initialization of
      'B<int, bool, MyType, char>'
   B         <int, bool, MyType, char> b{2, false,{4, "blub"}, 'c'};  // fails
                                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
vararg_constr.cpp:6:14: note: candidate constructor not viable: requires 2
      arguments, but 4 were provided
    explicit B(ArgTypes&&... args)

Хорошо, теперь мне очень, очень интересно то, что это работает для кортежей!В соответствии со стандартом (20.4.2.1) он имеет конструктор, который очень похож на мой.

template <class... Types>
class tuple {
public:
    // ...

    template <class... UTypes>
    explicit tuple(UTypes&&...);

    // ...
};

При построении объекта кортежа таким же образом, он работает!

ТеперьЯ хотел бы знать:

А) Какого черта?Почему std :: tuple такой особенный, и почему компиляторы не определяют правильное количество аргументов?

B) Как я могу заставить эту работу работать?

1 Ответ

6 голосов
/ 04 марта 2012

A) Почему компилятор должен знать, что {4, "blub"} имеет тип MyType, а не tuple<int, const char*>?

B) Измените ArgTypes на Ts в конструкторе:

explicit B(Ts&&... args)

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

  explicit constexpr tuple(const _Elements&... __elements);

EDIT : Дело в том, что вызывается конструктор с const &, а не с R-значениями. Учтите следующее:

template <typename... Ts>
struct B {
  explicit B(const Ts&... elements) { std::cout << "A\n"; }
  template<typename... As,
           typename = typename std::enable_if<sizeof...(As) == sizeof...(Ts)>::type>
  explicit B(As&&... elements) { std::cout << "B\n" ;}
};

int main()
{
  MyType m {1, "blub"};
  B<int, char>           a{2, 'c'};                            // prints B
  B<bool, MyType, char>  b{false, {4, "blub"}, 'c'};           // prints A
  B<bool, MyType, MyType>c{false, {4, "blub"}, std::move(m)};  // prints A
}
...