Skip to content

Instantly share code, notes, and snippets.

@TheJJ
Last active August 17, 2017 16:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TheJJ/020f0375f2fdbc2d172096900c9c85cf to your computer and use it in GitHub Desktop.
Save TheJJ/020f0375f2fdbc2d172096900c9c85cf to your computer and use it in GitHub Desktop.
g++ compiler bug: linker error for a templated lambda function argument default
// 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