'How to detect if template argument is an std::initializer_list
In the following example, the last line fails because the conversion operator of JSON has to choose between two possibilities:
std::vector<int>::operator=(std::vector<int>&&)std::vector<int>::operator=(std::initializer_list<int>)
How can I constrain JSON::operator T() so it ignores the initializer_list overload?
struct JSON
{
using StorageType = std::variant<
int,
float,
std::string,
std::vector<int>,
std::vector<float>
>;
StorageType storage;
template<class T>
operator T() const {
return std::get<T>(storage);
}
};
int main(int argc, char* argv[])
{
const JSON json;
std::vector<int> numbers;
numbers = json; // more than one operator '=' matches these operands
return 0;
}
Solution 1:[1]
You can use enable_if to disable the conversion of initializer_list
template<class>
constexpr inline bool is_initializer_list = false;
template<class T>
constexpr inline bool is_initializer_list<std::initializer_list<T>> = true;
struct JSON
{
// ...
template<class T, std::enable_if_t<!is_initializer_list<T>>* = nullptr>
operator T() const {
return std::get<T>(storage);
}
};
Solution 2:[2]
While the question for how to constrain get to exclude std::initializer_list is legitimate, I think what actually should be checked for is whether T is one of the types represented in the std::variant of StorageType.
You may check if a type is contained in the list of types of a std::variant as follows:
template <typename T, typename VariantT>
static inline constexpr bool variant_contains_type_v = false;
template <typename T, typename ... Ts>
static inline constexpr bool variant_contains_type_v<T, std::variant<Ts...>> = std::disjunction_v<std::is_same<T, Ts>...>;
Your implementation may then be adapted to
template<class T, typename = std::enable_if_t<variant_contains_type_v<T, StorageType>>>
operator T() const {
return std::get<T>(storage);
}
Solution 3:[3]
If you have access to C++20, you might use requires instead of SFINAE with std::enable_if:
// Some filtering traits
// Can even be turned into concept
template <typename>
constexpr inline bool is_initializer_list = false;
template <typename T>
constexpr inline bool is_initializer_list<std::initializer_list<T>> = true;
struct JSON
{
// ...
template <typename T>
operator T() const requires(!is_initializer_list<T>)
{
return std::get<T>(storage);
}
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | 康桓瑋 |
| Solution 2 | |
| Solution 3 | Jarod42 |
