Home > front end >  Why does passing lambda to constrained type template parameter result in `incomplete type` compiler
Why does passing lambda to constrained type template parameter result in `incomplete type` compiler

Time:12-23

I have a concept that helps me detect the signature of a function:

template <typename>
struct FuncHelper;

template <typename R, typename... Args>
struct FuncHelper<R(Args...)> {
    template <typename Func>
    static constexpr bool is_like = requires(const Func& f, Args... args) {
       { f(std::forward<Args>(args)...) } -> std::convertible_to<R>;
    };
};
template <typename FX, typename T>
concept function_like = FuncHelper<FX>::template is_like<T>;

I can use this concept in function overloads as a constraint:

template <typename T> requires function_like<bool(), T>
void testWorks(T&& func) { }
// I can call this like testWorks([&](){ return true; });

However if I specify this constraint within the template parameters:

template <function_like<bool()> T>
void testFails(T&& func) { }
// testFails([&](){ return true; }); // Compiler error: incomplete type

then I get a compiler error saying that I have an incomplete type.

Here is the full code:

#include <concepts>
#include <type_traits>
#include <utility>

using namespace std;

template <typename>
struct FuncHelper;

template <typename R, typename... Args>
struct FuncHelper<R(Args...)> {
    template <typename Func>
    static constexpr bool is_like = requires(const Func& f, Args... args) {
       { f(std::forward<Args>(args)...) } -> std::convertible_to<R>;
    };
};
template <typename FX, typename T>
concept function_like = FuncHelper<FX>::template is_like<T>;


template <typename T> requires function_like<bool(), T>
void testWorks(T&& func) { }

template <function_like<bool()> T>
void testFails(T&& func) { }

void testAll() {
    testWorks([&](){ return true; }); // No errors!
    testFails([&](){ return true; }); // Compiler error: incomplete type
}

You can try it out yourself:

GCC: https://godbolt.org/z/fG6c7E3qf

Clang: https://godbolt.org/z/7G7T8nTde

I thought that both testWorks and testFails were supposed to do the same thing. Where am I going wrong?

CodePudding user response:

These two:

template <typename T> requires function_like<bool(), T>
void testWorks(T&& func) { }

template <function_like<bool()> T>
void testFails(T&& func) { }

are not equivalent. The latter is equivalent to:

template <typename T> requires function_like<T, bool()>
void testFails(T&& func) { }

Note the different order of parameters into function_like.


The issue is that your concept isn't built to handle the type-constraint syntax properly. You need to flip your parameters:

template <typename T, typename Sig>
concept function_like = FuncHelper<Sig>::template is_like<T>;

and then both of these work fine (because now they actually are equivalent):

template <typename T> requires function_like<T, bool()>
void testWorks(T&& func) { }

template <function_like<bool()> T>
void testFails(T&& func) { }
  • Related