简体   繁体   中英

Template argument calculation at compile time

I'm trying to deduce the greater of two template arguments at compile time. Both template arguments are of type size_t.

I have a templated type, SomeType, which takes a size_t as it's template argument. I then have a function that takes two SomeType parameters with different template size_t's and i want the return type to be a SomeType with its templated size_t to be the greater of the two input size_t sizes.

template <size_t d> struct SomeType {...}

template<size_t d1, size_t d2>
SomeType<the_larger_of_d1_and_d2> Func(SomeType<d1> A, SomeType<d2> B)
{
    ...
}

Is this possible?

You can compute the type directly, no need for SFINAE:

template<size_t d1, size_t d2>
SomeType<(d1 > d2 ? d1 : d2)> Func(SomeType<d1> A, SomeType<d2> B)
{
    …
}

The solution by @KonradRudolph is correct of course. But if you want to delve further in to template metaprogramming, it would pay-off very quickly to learn Boost.MPL . It provides a whole battery of convenience functions. Eg your question can be solved like

#include <iostream>
#include <boost/mpl/int.hpp>
#include <boost/mpl/max.hpp>

template<size_t d> 
struct SomeType
: 
    boost::mpl::int_<d> 
{};

template<size_t d1, size_t d2>
typename boost::mpl::max<SomeType<d1>, SomeType<d2> >::type
Func(SomeType<d1> A, SomeType<d2> B) 
{
    return typename boost::mpl::max<SomeType<d1>, SomeType<d2> >::type();
}

int main()
{
    SomeType<2> st2;
    SomeType<3> st3;
    boost::mpl::max<SomeType<2>, SomeType<3> >::type res = Func(st2, st3);
    std::cout << res.value;
}  

Live Example .

Some notes:

  • letting SomeType inherit from boost::mpl::int_ endows it with a type and value , as well as some convenient tags. This makes it very easy to re-use other metafunctions from Boost.MPL
  • the boost::mpl::max does the same ternary trick behind the scenes. It is more readible IMO, and if you ever want to change to another condition it's easy to do so.
  • there is a bit of a learning curve for Boost.MPL, but the tutorial at the linked documentation should get you started.

If you can make use of the standard, you can use SFINAE standard support:

template<size_t one, size_t two>
struct larger {
    static constexpr typename std::enable_if<(one > two), size_t>::type value() {
        return one;
    }

    static constexpr typename std::enable_if<(two >= one, size_t>::type value() {
        return two;
    }
};

Then

template<size_t d1, size_t d2>
SomeType<larger<d1, d2>::value()> Func(SomeType<d1> A, SomeType<d2> B)
{
    ...
}

As I'm constantly have to look up myself (my older code) about that question again and again, I've decided to make a GIT gist , and compilation sample that allows (at least me) to quickly access some 'template' code (pun intended), to play with the meta-programmed conditional type selection stuff (also working for the 'old' standard):

Selector declaration:

template<typename FalseType, typename TrueType, bool condition>
struct ConditionalTypeSelector {
    typedef void ResultType;
};

Selector specialization(s):

template<typename FalseType, typename TrueType>
struct ConditionalTypeSelector<FalseType,TrueType,false> {
    typedef FalseType ResultType;
};

template<typename FalseType, typename TrueType>
struct ConditionalTypeSelector<FalseType,TrueType,true> {
    typedef TrueType ResultType;
};

Selected types:

struct A {
    unsigned char member;
};

struct B {
    int member;
};

struct C {
    long long member;
};

Testing:

#include <iostream>
#include <typeinfo>

using namespace std;

int main() {
    cout << typeid
                ( ConditionalTypeSelector
                      < A,B,(sizeof(A) > sizeof(B))>::ResultType
                ).name() << endl;
    cout << typeid
                ( ConditionalTypeSelector
                      <A,B,(sizeof(B) > sizeof(A)) >::ResultType
                ).name() << endl;
    cout << typeid
                ( ConditionalTypeSelector
                      < A,C,(sizeof(A) > sizeof(C))>::ResultType
                ).name() << endl;
    cout << typeid
                ( ConditionalTypeSelector
                      < C,B,true>::ResultType
                ).name() << endl;
    cout << typeid
                ( ConditionalTypeSelector
                      < C,A,false>::ResultType
                ).name() << endl;

    return 0;
}

It's pretty easy to change this template to use eg an enum type for specialized selections, or whatever else constant condition known at compile time should be checked.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM