0

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.

paolo
  • 2,345
  • 1
  • 3
  • 17
ben yu
  • 11
  • 6
  • 1
    did you keep the `TTo` parameter on purpose (while return type is `int`)? – 463035818_is_not_an_ai Jul 25 '22 at 09:49
  • It doesn't matter. Usually I just need to specify `TTo` for the return type, regarding the `TFrom` usually it can be deduced from the parameter. All I want is to convert any one type to another type with `convert(SomeParam)` function call. When I add some constraints on `TTo` or `TFrom`, the most strictest version is picked. – ben yu Jul 25 '22 at 09:56
  • 1
    it does matter when caller calls `convert(someenum)` and expects to get a `X` but you return an `int` – 463035818_is_not_an_ai Jul 25 '22 at 09:57

1 Answers1

1

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.
paolo
  • 2,345
  • 1
  • 3
  • 17
  • perhaps also worth mentioning that there is no partial specialization for function templates. Though this isnt partial specialization anyhow – 463035818_is_not_an_ai Jul 25 '22 at 10:03
  • @paolo, Thanks for your response. I tried the second version: ``` template static std::enable_if_t, int> convert(TFrom const& from) { std::cout << "[X] From type: " << typeid(TFrom).name() << "\nTo type: " << typeid(TTo).name(); return 0; } ``` But got `ambiguous call to overloaded function` error. – ben yu Jul 25 '22 at 10:04
  • @463035818_is_not_a_number Yes, you're right. It should call function `overload`. – ben yu Jul 25 '22 at 10:09
  • @benyu You need to constrain the return type on _both_ the function implementations. – paolo Jul 25 '22 at 10:09