Reputation: 83
I have come across a strange error in the CI build of a project I'm working in. This only happens on a relatively old OS (Ubuntu 16.04) with a not-so-recent compiler (gcc 5.4.0).
The code causing the error is similar to the following: (https://godbolt.org/z/MhY6sv)
#include <limits>
#include <string>
#define DEFAULT_INIT(x) x{defaultValue<decltype(x)>}
namespace MyProject{
template<typename T>
class Arr1 : public std::array<T, 3>{
public:
Arr1(T t1, T t2, T t3) : std::array<T, 3>{{t1, t2, t3}}{}
};
template<typename T>
class Arr2 : public std::array<T, 3>{
Arr2(T t1, T t2, T t3) : std::array<T, 3>{{t1, t2, t3}}{}
};
namespace{
template <typename T> constexpr T defaultValue = std::numeric_limits<T>::max();
template <typename T> const Arr1<T> defaultValue<Arr1<T>> = Arr1<T>{defaultValue<T>, defaultValue<T>, defaultValue<T>};
template <typename T> const Arr2<T> defaultValue<Arr2<T>> = Arr2<T>{defaultValue<T>, defaultValue<T>, defaultValue<T>};
}
class S {
public:
S() : DEFAULT_INIT(_arr1), DEFAULT_INIT(_arr2), DEFAULT_INIT(_n) {}
S(unsigned int n) : DEFAULT_INIT(_arr1), DEFAULT_INIT(_arr2), _n{n} {}
private:
Arr1<unsigned int> _arr1;
Arr1<float> _arr2;
unsigned int _n;
};
}
int main(){
return 0;
}
which results in an assembler error
/tmp/ccWrYvQ2.s: Assembler messages:
/tmp/ccWrYvQ2.s:110: Error: symbol `_ZN9MyProject12_GLOBAL__N_1L12defaultValueE' is already defined
Playing around with compiler explorer (and given that other CI builds with more recent versions of gcc compile without problems) I've seen that this happens only for gcc < 6.2 and that it is due to the compiler generating the same symbol for both defaultValue<Arr1<T>>
and defaultValue<Arr2<T>>
.
Since in this project we still want to support (for now) the faulty OS with its default compiler, is there any workaround to get this to properly work?
Upvotes: 3
Views: 186
Reputation: 73206
We may minimize the OP's overly bloated example into:
template <typename T> struct A {};
template <typename T> const T defaultValue = 0;
template <typename T> const A<T> defaultValue<A<T>> = A<T>{};
struct S {
S() : a1{defaultValue<decltype(a1)>},
a2{defaultValue<decltype(a2)>} {}
A<char> a1;
A<int> a2;
};
int main() {
return 0;
}
and get the same symbol collision error on most GCC versions prior to GCC 6.2 (it successfully compiles on e.g. GCC 5.3 but has regressed in GCC 5.4).
This is GCC bug
where the root cause is Bug 69515 - partial specialization of variable templates is broken, both of which were resolved as of GCC 6.2.
Since in this project we still want to support (for now) the faulty OS with its default compiler, is there any workaround to get this to properly work?
If you want to stick with the older GCC compiler version, you will need to avoid using partial specialization of variable templates and instead e.g. wrap the value in a meta-function:
template <typename T> struct A {};
template <typename T>
struct default_value {
static const T value = 0;
};
template <typename T>
struct default_value<A<T>> {
static const A<T> value;
};
template <typename T>
const A<T> default_value<A<T>>::value = A<T>{};
template <typename T>
const T default_value_v = default_value<T>::value;
struct S {
S() : a1{default_value_v<decltype(a1)>},
a2{default_value_v<decltype(a2)>} {}
A<char> a1;
A<int> a2;
};
int main() {
return 0;
}
Upvotes: 4