Reputation: 352
I want to overload a method, based only on template argument (which is a type), without passing any method arguments. For that, I use code like this (simplified, of course) in C++17:
template<typename T>
class C {
public:
auto f() const {
return f((T*) nullptr);
}
private:
int f(int *) const {
return 42;
}
std::string f(std::string *) const {
return "hello";
}
std::vector<char> f(std::vector<char> *) const {
return {'h', 'i'};
}
};
dump(C<int>().f());
dump(C<std::string>().f());
dump(C<std::vector<char>>().f());
, which outputs:
42
"hello"
std::vector{'h', 'i'}
I am not a professional C++ developer, and this way looks hack-ish to me. Is there a better way to overload method f
without using pointers as helper arguments? Or is this a "normal" solution?
P.S.: I'm not bonded to any particular C++ version.
Upvotes: 0
Views: 95
Reputation: 72463
(The comments on the question raise some valid questions about why you would have this. But just focusing on the question as asked.)
It is a bit hacky, but sometimes having an API function call an implementation function for technical reasons is a reasonable approach. One gotcha you might get into with that pattern is that pointers can automatically convert to other pointers: particularly T*
to const T*
and Derived*
to Base*
.
A rewrite using if constexpr
(C++17 or later):
template <typename T>
T C<T>::f() const {
if constexpr (std::is_same_v<T, int>) {
return 42;
} else if constexpr (std::is_same_v<T, std::string>) {
// Note I changed the return type to T. If it's auto,
// this would need return std::string{"hello"} so you don't
// just return a const char*.
return "hello";
} else if constexpr (std::is_same_v<T, std::vector<char>>) {
return {'h', 'i'};
} else {
// Always false (but don't let the compiler "know" that):
static_assert(!std::is_same_v<T,T>,
"Invalid type T for C<T>::f");
}
}
No more worries about pointer conversions, and this gives you a nicer error message if T
is an unhandled type.
A rewrite using constraints (C++20 or later):
template<typename T>
class C {
public:
int f() const requires std::is_same_v<T,int> {
return 42;
}
std::string f() const requires std::is_same_v<T,std::string> {
return "hello";
}
std::vector<char> f() const
requires std::is_same_v<T, std::vector<char>>
{
return {'h', 'i'};
}
};
Now if a bad type T
is used, the class doesn't contain any member named f
at all! That could be better for other templates which might try checking if x.f()
is a valid expression. And if a bad type is used, the compiler error might list the overloads which were declared but discarded from the class.
Upvotes: 2