Templated function with defaulted std::function parameter causes symbol [...] already defined

Viewed 134

I just ran into something somewhat odd. I wonder if it's a bug, and if not I hope someone can explain the issue.

My issue is that when I create a templated function which takes an std::function as a defaulted parameter, I can only create one template instantiation of this function, otherwise I get an error.

Consider the following code:

#include <functional>

template<bool B>
void wut(std::function<void()> f = []() {})
{
    f();
}

int main() {
    wut<false>(); // works
    wut<false>(); // still works
    wut<true>();  // error
    return 0;
}

https://ideone.com/VlVcUv

When compiling this code I get the following error:

{standard input}: Assembler messages:
{standard input}:28: Error: symbol `_ZNSt14_Function_base13_Base_managerIUlvE_E10_M_managerERSt9_Any_dataRKS3_St18_Manager_operation' is already defined
{standard input}:127: Error: symbol `_ZNSt17_Function_handlerIFbvEUlvE_E9_M_invokeERKSt9_Any_data' is already defined
2 Answers

This is a gcc bug (which looks fixed after version 7.3) we can see this by looking at section [expr.prim.lambda.capture]p9:

A lambda-expression appearing in a default argument shall not implicitly or explicitly capture any entity. [Example:

void f2() {
int i = 1;
void g1(int = ([i]{ return i; })()); // ill-formed
void g2(int = ([i]{ return 0; })()); // ill-formed
void g3(int = ([=]{ return i; })()); // ill-formed
void g4(int = ([=]{ return 0; })()); // OK
void g5(int = ([]{ return sizeof i; })()); // OK
}

—end example]

As an alternative to IDEOne, you can use Wandbox which keeps up to date with gcc and clang versions, see your example live there.

Looks like a compiler bug.

However using lambdas in header files is fraught with ODR issues. So I'd avoid it.

In :

template<auto X>
struct always_return {
  template<class...Args>
  auto operator()(Args&&...)const{ return X; }
};

template<bool B>
void wut(std::function<bool()> f = always_return<false>{})
{

Or in :

template<class T>
struct always_return_t {
  T t;
  template<class...Args>
  auto operator()(Args&&...)const{ return t; }
};
template<class T>
always_return_t<typename std::decay<T>::type> always_return(T&&t) {
  return {std::forward<T>(t)};
}
template<bool B>
void wut(std::function<bool()> f = always_return(false)){
Related