Ошибка замены для аргумента шаблона шаблона - PullRequest
3 голосов
/ 10 марта 2019

Я хочу, чтобы вспомогательная функция создавала для меня класс. В настоящее время он не может компилироваться в clang (хотя он компилирует работу в gcc), но мне нужно, чтобы он работал и в clang. В настоящее время я использую clang version 6.0.0-1ubuntu2.

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

#include <vector>

using namespace std;

template <typename T, template <typename> typename Container>
struct SomeClass {
    SomeClass(const Container<T>& c) {
    }
};

template <typename T, template <typename> typename C>
inline auto make_some_class(const C<T>& container) {
    return SomeClass<T, C>(container);
}

int main() {
    vector<int> ints;

    auto stuff = make_some_class(ints);  
}

main.cpp: 19: 18: ошибка: нет подходящей функции для вызова 'make_some_class'

   auto stuff = make_some_class(ints);  

                ^~~~~~~~~~~~~~~

main.cpp: 12: 13: примечание: шаблон кандидата игнорируется: ошибка замещения [с T = int]: аргумент шаблона шаблона имеет параметры шаблона, отличные от соответствующего параметра шаблона шаблона

inline auto make_some_class(const C<T>& container) {

            ^

1 сгенерированная ошибка.

Ответы [ 3 ]

3 голосов
/ 10 марта 2019

Предложение: попробуйте с

#include <vector>

template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
    SomeClass(const Container<Ts...>& c) {
    }
};

template <template <typename...> typename C, typename ... Ts>
inline auto make_some_class(const C<Ts...>& container) {
    return SomeClass<C, Ts...>(container);
}

int main() {
    std::vector<int> ints;

    auto stuff = make_some_class(ints);  
}

Я имею в виду ... Полагаю, проблема в том, что std::vector не является контейнером, который получает один параметр шаблона типа; это контейнер, который получает параметр шаблона типа two (второй с типом по умолчанию: std::allocator<T>, где T - первый).

Таким образом, предложение: сделать SomeClass более гибким и способным получать контейнер с переменным списком типов шаблонов аргументов

template <typename...> typename Container

и соответствующий список типов шаблонов

typename ... Ts

Если вам нужен переменный список аргументов (Ts...), он нужен вам в последней позиции, поэтому вам нужно поменять позицию Container и T (теперь Ts...): до Container и после Вариационный список Ts...

template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
    SomeClass(const Container<Ts...>& c) {
    }
};

Строго не требуется, но для единообразия предлагаю переписать make_some_class() таким же образом (и, очевидно, передать C перед Ts... в списке параметров шаблона).

template <template <typename...> typename C, typename ... Ts>
inline auto make_some_class(const C<Ts...>& container) {
    return SomeClass<C, Ts...>(container);
}
2 голосов
/ 10 марта 2019

Давайте уменьшим это до:

template <template <typename> class C, typename T>
void foo(C<T> const&) { }

std::vector<int> v;
foo(v);

Вы обнаружите, что gcc компилирует это, а clang - нет.Причины обоих интересны.

Во-первых, напомним, что std::vector - это шаблон класса, который принимает два параметров шаблона:

template <typename T, typename Alloc = std::allocator<T>>
class vector { ... };

Почему gcc считает, чтосоответствует template <typename> class C - шаблон, который имеет только один параметр шаблона?Потому что правила изменились в результате P0522R0 .И это имеет смысл - мы можем использовать vector, как если бы он имел один параметр типа в обычном коде, поэтому он должен быть в состоянии соответствовать такому параметру шаблона.

Теперь, почему Clang считает, что vector не не соответствует?Потому что они явно решили не принимать это правило.Начиная с их документов :

(10): Несмотря на то, что это разрешение для отчета о дефектах, эта функция по умолчанию отключена во всех языковых версиях и может быть включена явно с помощьюфлаг -frelaxed-template-template-args в Clang 4 года.Изменение в стандарте не содержит соответствующего изменения для частичного упорядочения шаблона, что приводит к ошибкам неоднозначности для разумного и ранее действительного кода.Ожидается, что эта проблема будет исправлена ​​в ближайшее время.

То есть она может нарушить код .

Конечно, вы можете просто захотеть узнать, как это исправить.Просто измените объявление шаблона шаблона параметра C:

template <template <typename...> class C, typename T>
void foo(C<T> const&) { }

std::vector<int> v;
foo(v); // ok in both gcc and clang
2 голосов
/ 10 марта 2019

Как уже предлагалось в комментарии и max66, std::vector имеет два параметра шаблона, value_type и allocator_type. До C ++ 17 мы должны явно записать оба параметра в качестве параметров шаблона шаблона в этом случае следующим образом, и это также будет работать в C ++ 17:

DEMO

template <typename T, template <typename V, typename Allocator = std::allocator<T>> typename Container>
struct SomeClass {
    SomeClass(const Container<T>& c) {
    }
};

template <typename T, template <typename V, typename Allocator = std::allocator<T>> typename C>
inline auto make_some_class(const C<T>& container) {
    return SomeClass<T, C>(container);
}

Кстати, начиная с C ++ 17, "Соответствие параметров шаблона шаблона совместимым аргументам" [P0522R0] принято, и ваш код прав, если вы используете C ++ 17. Но Clang по-прежнему отключает эту новую функцию по умолчанию, и флаг компиляции -frelaxed-template-template-args включает эту функцию.

DEMO

...