Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struct that contains an alias to the first convertible type of a variadic pack [duplicate]

I have typename T1 and I have a parameter pack typename... Variadic.

I want to create a struct that contains a using alias using Type = ... to the first type in the parameter pack that T1 can be converted into. So far I have tried the following:

template<typename T1, typename T2, typename... Variadic>
struct VariadicConvertibleType
{
    using Type = std::enable_if<std::is_convertible<T1, T2>::value, T2>::type;
};

This could be a potential solution using SFINAE for the first two types, but I need to expand this to all the types in the pack using recursion. All my attempts failed so far, because you can't put conditionals into using alias declarations. Otherwise something similar to this could be used:

template<typename T1, typename T2, typename... Variadic>
struct VariadicConvertibleType
{
    using Type = std::is_convertible<T1, T2>::value ? T2 : VariadicConvertibleType<T1, Variadic...>::Type;
};

I can use everything up to (and including) C++14 to implement the solution. I can not using anything other than the standary library though.

like image 945
Krisztián Szabó Avatar asked Jul 31 '17 13:07

Krisztián Szabó


People also ask

What is Variadic template in C++?

Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration.

How do you use variadic arguments?

It takes one fixed argument and then any number of arguments can be passed. The variadic function consists of at least one fixed variable and then an ellipsis(…) as the last parameter. This enables access to variadic function arguments. *argN* is the last fixed argument in the variadic function.

What is the use of Variadic templates?

With the variadic templates feature, you can define class or function templates that have any number (including zero) of parameters. To achieve this goal, this feature introduces a kind of parameter called parameter pack to represent a list of zero or more parameters for templates.

What is a parameter pack C++?

Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.


1 Answers

You can use std::conditional, like this:

template<typename T1, typename T2, typename... Variadic>
struct VariadicConvertibleType
{
    using type = std::conditional_t<std::is_convertible<T1, T2>::value, T2, typename VariadicConvertibleType<T1, Variadic...>::type>;
};

template<typename T1, typename T2>
struct VariadicConvertibleType<T1, T2>
{
    static_assert(std::is_convertible<T1, T2>::value);
    using type = T2;


    // Alternative base-case
    // using type = std::conditional_t<std::is_convertible<T1, T2>::value, T2, T1>;
};

I have provided two base-cases (alternative one in comment). The primary one (which I thing is what you want) uses a (C++14) static_assert if T1 is not convertible to any of the types in Variadic. The alternative base-case sets type to T1 in that case.

Test

#include <iostream>
#include <type_traits>
#include <typeinfo>

int main()
{
    using my_type_1 = typename VariadicConvertibleType<int, double, float>::type;
    std::cout << typeid(my_type_1).name() << '\n'; // double

    using my_type_2 = typename VariadicConvertibleType<int, int*, float>::type;
    std::cout << typeid(my_type_2).name() << '\n'; // float

    using my_type_3 = typename VariadicConvertibleType<int, int*, float*>::type;
    std::cout << typeid(my_type_3).name() << '\n'; // Complile error with the primary base-case, and int with the alternative base-case.
}
like image 149
Jonas Avatar answered Oct 04 '22 20:10

Jonas