Наблюдение Эвга бьет ногтем по голове: проблема здесь в ADL. Это на самом деле та же проблема, с которой я столкнулся с этим вопросом .
Проблема заключается в следующем: у нас здесь неквалифицированный звонок:
template<class ExistingInput, class ... NewInputs>
using Added = decltype(addedHelper<NewInputs...>(std::declval<ExistingInput>()));
// ^^^^^^^^^^^
Мы знаем, что это шаблон функции, потому что мы находим его в обычном поиске, поэтому нам не нужно разбираться со всем вопросом «является ли <
оператор или вводчик шаблона». Однако, поскольку это неквалифицированный вызов, мы также должны выполнить поиск, зависящий от аргумента.
ADL нужно изучить связанные пространства имен всех аргументов, что выглядит нормально - нам не нужны полные типы для этого. Но ADL также должен искать потенциальные функции-друзья и шаблоны функций, определенные в классах. Ведь для этого нужно работать:
struct X {
friend void foo(X) { }
};
foo(X{}); // must work, call the hidden friend defined within X
В результате в нашем звонке идет речь:
auto test(derived<A, B> in) -> Added<decltype(in), C>;
Мы должны создать экземпляр derived<A, B>
... но этот тип наследуется от двух неполных классов, что мы не можем сделать. Вот где проблема, вот где мы терпим неудачу.
Вот почему работает версия предварительной декларации. template <typename... T> struct derived;
неполно, поэтому просто попытаться заглянуть внутрь него для поиска функций-друзей, тривиально ничего не находит - нам не нужно создавать что-либо еще.
Аналогично, версия, в которой derived
была завершена, но на самом деле ничего не происходила, также будет работать.
<ч />
К счастью, это можно исправить в связи с тем, что предложил Эвг. Сделайте квалифицированный звонок:
template<class ExistingInput, class ... NewInputs>
using Added = decltype(::addedHelper<NewInputs...>(std::declval<ExistingInput>()));
Это позволяет избежать ADL, который вы даже не хотели. В лучшем случае вы избегаете делать то, что не приносит вам никакой пользы. Плохой случай, ваш код не компилируется. Злой случай, для некоторых входов вы случайно вызываете другую функцию полностью.
<Ч />
Или просто используйте Boost.Mp11 mp_push_back