Home > other >  Function template overload with constraints
Function template overload with constraints

Time:07-25

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 concept can add constraints, but let's assume it's 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 of convert, if you always return int.
  • Related