Last active
August 17, 2017 16:28
-
-
Save TheJJ/020f0375f2fdbc2d172096900c9c85cf to your computer and use it in GitHub Desktop.
g++ compiler bug: linker error for a templated lambda function argument default
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// writing a templated lambda as a default value for a function argument | |
// causes linker errors if the function is templated two times, | |
// when the resulting lambda signature ends up being the same despite the function template is different. | |
// | |
// breaks with: | |
// g++ -std=gnu++14 -Wall -Wextra -pedantic test.cpp | |
// | |
// works with llvm: | |
// clang++ -std=gnu++14 -Wall -Wextra -pedantic test.cpp | |
#define ENABLE_BUG true | |
#include <functional> | |
#include <iostream> | |
#include <set> | |
#include <sstream> | |
#include <string> | |
#include <vector> | |
// the default lambda function ends up in the object file | |
// multiple times, which creates linker conflicts | |
template <typename T> | |
std::string strjoin_broken(const std::string &delim, | |
const T &container, | |
const std::function<std::string(const typename T::value_type &)> & | |
func=[](const typename T::value_type &in) -> std::string {return in;}) { | |
std::ostringstream builder; | |
size_t cnt = 0; | |
for (auto &entry : container) { | |
if (cnt > 0) { | |
builder << delim; | |
} | |
builder << func(entry); | |
cnt += 1; | |
} | |
return builder.str(); | |
} | |
// this is the function which does the exact same as the default lambda above | |
// except it does not cause linker errors. | |
template <typename T> | |
std::string get_elem(const T &in) { | |
return in; | |
} | |
template <typename T> | |
std::string strjoin_works(const std::string &delim, | |
const T &container, | |
const std::function<std::string(const typename T::value_type &)> & | |
func=&get_elem<typename T::value_type>) { | |
std::ostringstream builder; | |
size_t cnt = 0; | |
for (auto &entry : container) { | |
if (cnt > 0) { | |
builder << delim; | |
} | |
builder << func(entry); | |
cnt += 1; | |
} | |
return builder.str(); | |
} | |
int main() { | |
std::vector<std::string> vec{ | |
"test", | |
"moar", | |
"stuff" | |
}; | |
// if you change this type to std::vector the bug will go away. | |
std::set<std::string> set{ | |
"to", | |
"catch", | |
"bugs" | |
}; | |
#if ENABLE_BUG | |
// if you remove one of the lines, it will work. | |
std::cout << "vec:" << strjoin_broken(", ", vec) << std::endl; | |
std::cout << "set:" << strjoin_broken(", ", set) << std::endl; | |
#else | |
std::cout << "vec:" << strjoin_works(", ", vec) << std::endl; | |
std::cout << "set:" << strjoin_works(", ", set) << std::endl; | |
#endif | |
return 0; | |
} | |
//////////////////// | |
// minimal example | |
//////////////////// | |
struct A { | |
template<typename T> A(T) {} | |
}; | |
template<typename T> | |
void stuff(A = []{}) {} | |
int main() { | |
stuff<int>(); | |
stuff<unsigned int>(); | |
} | |
// the lambda is generated multiple times with the same name. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment