Reputation: 4435
I have a template class. Some methods can only be called for certain template parameters, otherwise compilation error occurs. Here, if you call C<int>{}.vectorMethod()
you get a compilation error that int
does not have a .size()
method.
template <class T>
struct C
{
void intMethod()
{
cout << T{} + 1 << "\n";
}
void vectorMethod()
{
cout << T{}.size() << "\n";
}
};
int main() {
C<int> a;
a.intMethod();
// a.vectorMethod(); // compilation error
C<vector<int>> b;
b.vectorMethod();
}
I want to make an explicit instantiation of this template for some frequently used types and still be able to get a compilation error if I try to use a method unsuitable for a certain type. Ideally, I'd like to have the following:
// header.h
template <class T>
struct C
{
void intMethod()
{
cout << T{} + 1 << "\n";
}
void vectorMethod()
{
cout << T{}.size() << "\n";
}
};
extern template struct C<int>;
extern template struct C<vector<int>>;
// impl.cpp
#include "header.h"
template struct C<int>;
template struct C<vector<int>>;
// main.cpp
#include "header.h"
int main() {
C<int> a;
a.intMethod();
// a.vectorMethod(); // compilation or linker error is desired
C<vector<int>> b;
b.vectorMethod();
}
The first problem is that explicit instantiation in impl.cpp
fails to compile since it tries to instantiate all methods of the class. The second is that in main.cpp
the compiler notes the extern template
and does not try to compile C<int>::vectorMethod
; that's why I'm fine with linker error instead of compilation one.
We can use concepts to get rid of the first problem:
template <class T>
struct C
{
void vectorMethod()
{
if constexpr (requires { T{}.size(); }) {
cout << T{}.size() << "\n";
}
}
};
Now impl.cpp
compiles, but I can erroneously call C<int>::vectorMethod()
and it will silently do nothing.
I also tried to use enable_if:
template <class T>
struct C
{
std::enable_if_t<std::is_same_v<T, vector<int>>> vectorMethod()
{
cout << T{}.size() << "\n";
}
};
However, template class C<int>
also fails to compile in this case.
Is it possible to achieve what I'm trying to?
Upvotes: 0
Views: 67
Reputation: 217255
With c++20 requires
, you might do
template <class T>
struct C
{
void intMethod() requires(requires {T{} + 1;})
{
std::cout << T{} + 1 << "\n";
}
void vectorMethod() requires(requires {T{}.size();})
{
std::cout << T{}.size() << "\n";
}
};
Demo (Not sure why clang still fails).
Upvotes: 3