Как экспортировать ctor с ограничениями SFINAE, определенный в файле cpp, для явно созданного класса шаблона?

Вот моя попытка:

  • Заголовок, определяющий класс шаблона Foo и объявляющий, но не определяющий ctor только для данного значения NTTP N,
    // foo.hpp
    #include <type_traits>
    
    template<int N>
    struct Foo {
        template<int M = N, std::enable_if_t<M == 2, int> = 0>
        Foo(int);
    };
    
  • файл cpp, определяющий этот ctor и явно создающий экземпляр Foo для значения, которое соответствует ctor, не выводимому из SFINAE,
    // foo.cpp
    #include "foo.hpp"
    
    template<int N>
    template<int M, std::enable_if_t<M == 2, int>>
    Foo<N>::Foo(int) {}
    
    template struct Foo<2>;
    
  • основной TU, который создает экземпляр объекта класса Foo<2>,
    // main.hpp
    #include "foo.hpp"
    
    int main() {
        Foo<2>{0};
    }
    

Если я попытаюсь скомпилировать два приведенных выше TU и связать их, я получу следующую ошибку от компоновщика:

main.cpp:(.text+0x24): undefined reference to `Foo<2>::Foo<2, 0>(int)'
collect2: error: ld returned 1 exit status

Действительно, я не вижу символов foo.cpp, так как это

nm foo.o

не дает вывода.


Если я добавлю еще один, неограниченный ctor, например.

// foo.hpp
// …
struct Foo {
    Foo(int, int);
// …

и

// foo.cpp
// …
template<int N>
Foo<N>::Foo(int, int) {}
// …

тогда я получаю то, что ожидаю:

nm foo.o | c++filt
0000000000000000 W Foo<2>::Foo(int, int)
0000000000000000 W Foo<2>::Foo(int, int)
0000000000000000 n Foo<2>::Foo(int, int)

Основная проблема заключается в том, что параметр шаблона конструктора не выводим, так что Foo<2>{0} это означает? Какой шаблон конструктора следует выбрать (игнорируя SFINAE), как на самом деле выбирается этот ситаксис Foo<2>::Foo<2, 0>(int)? Вам нужен аргумент конструктора, который будет использовать параметр шаблона конструктора, чтобы его можно было вывести.

Marek R 02.09.2024 18:33

Здесь я сделал ваш пример компиляции и компоновки, но это похоже на ошибку gcc (IMO вообще не должен компилироваться).

Marek R 02.09.2024 18:37

@MarekR, но в объявлении конструктора есть int M = N, так зачем же выводить M? В конце концов, если я перенесу определение в заголовок и оставлю в cpp только #include "foo.hpp" и template struct Foo<2>;, ссылка сработает.

Enlico 02.09.2024 18:39

Зачем вам вообще M, учитывая, что для M невозможно указать какое-либо значение, кроме N? Обычно у нас есть ограничения enable_if для предотвращения некоторых экземпляров, но в этом случае все экземпляры, которые он предотвращает, — это те, которые вы даже не можете запросить.

Brian Bi 04.09.2024 00:38

@BrianBi, я не уверен, что понимаю, но N — это параметр шаблона класса, так как я могу отключить ctor только для одного значения N, не создавая шаблон самого ctor, чтобы использовать SFINAE? Да, M всегда == N, но это параметр шаблона актера, а не класса, поэтому я могу использовать его для SFINAE-выхода актера.

Enlico 04.09.2024 14:08

О, я только что увидел, что это C++17, теперь я понял, вы не можете использовать requires.

Brian Bi 04.09.2024 14:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Явное создание экземпляра класса шаблона не создает экземпляр функции-члена шаблона.

Вам также необходимо явно создать экземпляры этих методов:

// template Foo<2>::Foo<2, 0>(int); // Not supported by clang
template Foo<2>::Foo(int);

ДемоДемо

Я пытался это сделать, но даже не пытался скомпилировать, так как меня отпугнули красные закорючки Кланга. Но да, я вижу, что это работает для GCC! К сожалению, мне нужен код, который компилируется на Clang, GCC, MSVC, поэтому возникает вопрос: правильно ли Clang отклоняет этот код?

Enlico 03.09.2024 09:31

Кланг согласен с template Foo<2>::Foo(int);

Jarod42 03.09.2024 09:39

Другие вопросы по теме