Вот моя попытка:
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);
};
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>;
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)
Здесь я сделал ваш пример компиляции и компоновки, но это похоже на ошибку gcc (IMO вообще не должен компилироваться).
@MarekR, но в объявлении конструктора есть int M = N
, так зачем же выводить M
? В конце концов, если я перенесу определение в заголовок и оставлю в cpp только #include "foo.hpp"
и template struct Foo<2>;
, ссылка сработает.
Зачем вам вообще M
, учитывая, что для M
невозможно указать какое-либо значение, кроме N
? Обычно у нас есть ограничения enable_if
для предотвращения некоторых экземпляров, но в этом случае все экземпляры, которые он предотвращает, — это те, которые вы даже не можете запросить.
@BrianBi, я не уверен, что понимаю, но N
— это параметр шаблона класса, так как я могу отключить ctor только для одного значения N
, не создавая шаблон самого ctor, чтобы использовать SFINAE? Да, M
всегда == N
, но это параметр шаблона актера, а не класса, поэтому я могу использовать его для SFINAE-выхода актера.
О, я только что увидел, что это C++17, теперь я понял, вы не можете использовать requires
.
Явное создание экземпляра класса шаблона не создает экземпляр функции-члена шаблона.
Вам также необходимо явно создать экземпляры этих методов:
// template Foo<2>::Foo<2, 0>(int); // Not supported by clang
template Foo<2>::Foo(int);
Я пытался это сделать, но даже не пытался скомпилировать, так как меня отпугнули красные закорючки Кланга. Но да, я вижу, что это работает для GCC! К сожалению, мне нужен код, который компилируется на Clang, GCC, MSVC, поэтому возникает вопрос: правильно ли Clang отклоняет этот код?
Кланг согласен с template Foo<2>::Foo(int);
Основная проблема заключается в том, что параметр шаблона конструктора не выводим, так что
Foo<2>{0}
это означает? Какой шаблон конструктора следует выбрать (игнорируя SFINAE), как на самом деле выбирается этот ситаксисFoo<2>::Foo<2, 0>(int)
? Вам нужен аргумент конструктора, который будет использовать параметр шаблона конструктора, чтобы его можно было вывести.