Fully specialized overloaded method is not called when used as template default parameter

Viewed 46

I'd like to be able to specify default non-type template parameter by calling one of overloaded fuctions (that are template with full specialization). Following code represents the problem: I expecet FieldType2 getDefaultField<FieldType2>() to be printed, but Called getDefaultField() ! is printed.

#include <iostream>

enum class FieldType1 {
    Description1,
    Description2,
    Description3
};

enum class FieldType2 {
    Description1,
    Description2,
    Description3
};

template<class FiledType>
struct FieldDescription {
    constexpr static int startPos{0};
    constexpr static FieldType fieldType{}; 
};

struct ConcreteField2 : public FieldDescription<FieldType2> {};

template<class FieldType>
constexpr FieldType getDefaultField() {
    return FieldType{};
};

template<>
constexpr FieldType1 getDefaultField<FieldType1>() {
    return FieldType1::Description1;
};

template<>
constexpr FieldType2 getDefaultField<FieldType2>() {
    return FieldType2::Description3;
};

template<class FieldDescr,
        decltype(FieldDescr::fieldType) fieldType = getDefaultField<decltype(FieldDescr::fieldType)>()>
void process() {
    if (fieldType == FieldType2::Description3) {
        std::cout << "FieldType2 getDefaultField<FieldType2>()" << std::endl;
    }
    if (fieldType == FieldType2::Description1) {
        std::cout << "Called getDefaultField() !" << std::endl;
    }
}

int main() {
    process<ConcreteField2>();

    return 0;
}   
2 Answers

decltype(FieldDescr::fieldType) is const FieldType2 not FieldType2. So your specialisation doesn't match and primary template is used.

You might use std::decay_t:

getDefaultField<std::decay_t<decltype(FieldDescr::fieldType)>>()

Demo

But as suggested in comment, that usage of dummy member is strange, a regular using type = FiledType; is more idiomatic.

I'd suggest don't use decltype and decay_t and use using to alias FieldType instead:

template<class FT>
struct FieldDescription {
    using FieldType = FT;
    constexpr static int startPos{0};
    // constexpr static FieldType fieldType{}; // You can get rid of that member,
                                               // unless used elsewhere
};

and

template <class FieldDescr,
         typename FieldDescr::FieldType fieldType =
             getDefaultField<typename FieldDescr::FieldType>()>
Related