Rumburak Rumburak - 1 month ago 13
C++ Question

Why does decltype return type fail for recursive template, while return type deduction works just fine?

While working on a C++11 type-set, I tried to implement this function (stripped down to the minimum):

constexpr auto test() -> bool;

template <typename T, typename... Rest>
constexpr auto test() -> decltype(test<Rest...>())
{
return {};
}


Both gcc and clang choke on this. Clang says:

test.cpp:54:40: error: 'Rest' does not refer to a value
constexpr auto test() -> decltype(test<Rest...>())
^


gcc complains:

test.cpp:54:44: error: expected primary-expression before ‘...’ token
constexpr auto test() -> decltype(test<Rest...>())


I guess this is because the variadic version of test is not even fully declared when the
decltype
looks at it.

However, when I use return type deduction in C++14, this compiles just fine:

constexpr auto test() -> bool;

template <typename T, typename... Rest>
constexpr auto test()
{
return test<Rest...>();
}


Seems like
test
is considered sufficiently declared here.

I wonder why this doesn't work for the
decltype
variant? Even if I turn on C++14 support?

PS: It turns out, that I cannot really call even the C++14 function, so maybe the whole thing is botched...

Answer

One problem is that your first test overload is not a function template, so can't be called with test<>() syntax.

However, decltype doesn't really work with recursive variadic functions like that, since the return type is part of the declaration of the function, so that overload is not declared when the name is looked up.

You can get around this in C++11 by using template classes instead:

template <typename... Ts>
struct test;

template <typename T, typename... Ts>
struct test<T,Ts...> {
    static decltype(test<Ts...>::value()) value() { return test<Ts...>::value(); }   
};

template <>
struct test<> {
    static bool value() { return true; }
};

Live demo


In C++14 you can make the first overload take a single template parameter and the second take two and a parameter pack:

template <typename T>
constexpr auto test() -> bool { return true; }

template <typename T, typename U, typename... Rest>
constexpr auto test()
{
  return test<U,Rest...>();
}

Live demo

Comments