I have the following code:
// converters.h
#pragma once
#include <iostream>
#include <type_traits>
template <typename TTo, typename TFrom>
static TTo convert(const TFrom& from)
{
TTo t = static_cast<TTo>(from);
std::cout << "From type: " << typeid(TFrom).name() << "\nTo type: " << typeid(TTo).name();
return t;
}
template<typename TTo, typename TFrom>
static int convert(std::enable_if_t<std::is_enum_v<TFrom>, TFrom> const& from)
{
std::cout << "[X] From type: " << typeid(TFrom).name() << "\nTo type: " << typeid(TTo).name();
return 0;
}
// Main.cpp
#include "converters.h"
enum class Season
{
Spring,
Summer,
Autumn,
Winter
};
int main()
{
convert<int>(Season::Spring);
return 0;
}
I want to add some constraints to TFrom
or TTo
or both as function template specialization, but somehow it's not working. The more stricter version of the function template specialization is not getting called.
In the above example, I expect the second one is getting called, however, it is still calling the primary one.
How can I add constraints to function template specialization?
What if TTo
or TFrom
is STL types, like std::unordered_map<TKey, TVal>
? What I want to do is to convert from any type to the other type with this convert
function call. If the primary template doesn't meet the need, then just add a new specialization. Anyone can give some guidance on how to achieve this purpose? Thanks. Any input is appreciated.
Note: I know some c 20 concept can add constraints, but let's assume it's c 17 due to some reason.
CodePudding user response:
In this function declaration:
template<typename TTo, typename TFrom>
static int convert(std::enable_if_t<std::is_enum_v<TFrom>, TFrom> const& from);
type TFrom
appears in a non-deduced context. So, when you call:
convert<int>(Season::Spring);
the compiler can only call (due to SFINAE)
template <typename TTo, typename TFrom>
static TTo convert(const TFrom& from);
You may get the behavior you expect by SFINAE-ing on the return type, i.e.:
template <typename TTo, typename TFrom>
static std::enable_if_t<!std::is_enum_v<TFrom>, TTo> convert(TFrom const& from) {
TTo t = static_cast<TTo>(from);
std::cout << "From type: " << typeid(TFrom).name()
<< "\nTo type: " << typeid(TTo).name();
return t;
}
template <typename TTo, typename TFrom>
static std::enable_if_t<std::is_enum_v<TFrom>, int> convert(TFrom const& from) {
std::cout << "[X] From type: " << typeid(TFrom).name()
<< "\nTo type: " << typeid(TTo).name();
return 0;
}
Notes:
- You need to constrain the return type on both the function templates, otherwise
convert<int>(Season::Spring)
would result in an ambiguous overload call; - Function templates cannot be partially specialized (even though you're not partially specializing it, anyway);
- As noted in the comments, there's no need for
typename TTo
in the second version ofconvert
, if you always returnint
.