Undefined reference only when using clang
I am writing a header to handle units of length, mass and time and derived units of these (e.g. feet per year). My code so far compiles fine on g++ but using clang++-3.8 i get this error
clang++-3.8 -stdlib=libstdc++ --std=c++1y -Wall -Wextra -Wshadow -pedantic -Werror -g test/test.cpp -I include -o bin/test
/tmp/test-8620db.o: In function `Unit<&units::none_c, &units::none_c, &units::none_c>':
/home/andrew/Documents/units/include/units.h:39: undefined reference to `units::Unit<1, 0, -1, &units::none_c, &units::none_c, &units::none_c>::scale'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Below is the bulk of my code. The class Unit takes 6 template parameters. The first 3 indicate the dimensions of the type . The scale parameters represent the size of the unit compared to the standard. The problematic part is where I try to put these together into a static constexpr double scale.
#ifndef _units_h_
#define _units_h_
namespace units {
namespace { // compile time integer power function
constexpr double ipow_pos(double x, int n) {
return n == 0 ? 1 : ipow_pos(x, --n) * x;
}
constexpr double ipow_neg(double x, int n) {
return n == 0 ? 1 : ipow_neg(x, ++n) / x;
}
constexpr double ipow(double x, int n) {
return n > 0 ? ipow_pos(x, n) : ipow_neg(x, n);
}
}
// Some conversions
extern constexpr double none_c = 1.0;
extern constexpr double mile_c = 1609.34;
extern constexpr double hour_c = 3600.0;
template<int d_pow, int m_pow, int t_pow, const double* d_scale = &none_c,
const double* m_scale = &none_c, const double* t_scale = &none_c>
struct Unit {
// Default value (1 SI unit)
Unit()
: data(1 / scale) {
}
// Create from double (assumed to be in SI units)
Unit(double x)
: data(x / scale) {
}
// Convert from unit of same dimensions but different scale
template <const double* d, const double* m, const double* t>
Unit(const Unit<d_pow, m_pow, t_pow, d, m, t>& other)
: data(other.data * other.scale / scale) {
}
double data;
// the size of the unit relative to the standard unit
constexpr static double scale = ipow(*d_scale, d_pow)
* ipow(*m_scale, m_pow)
* ipow(*t_scale, t_pow);
};
typedef Unit<1, 0, 0> metres;
typedef Unit<0, 1, 0> kilograms;
typedef Unit<0, 0, 1> seconds;
typedef Unit<1, 0, -1, &mile_c, &none_c, &hour_c> mph;
typedef Unit<1, 0, -1> mps;
} // namespace units
#endif
And my main()
#include "units.h"
#include <iostream>
int main() {
units::mps speed(5.0);
units::mph converted(speed); // this line fails
std::cout << converted.data << '\n';
}
For some reason when I try to print the problematic member directly, like below, clang happily spits out the correct value. What is the problem here?
#include "units.h"
#include <iostream>
int main() { // this works
std::cout << units::Unit<1, 0, -1, &units::none_c, &units::none_c, &units::none_c>::scale << '\n';
}
Posted Aug 7, 2016, 3:25 PM UTC · edited
It's being ODR-used, so you need to provide an out-of-line definition, like:
What do you mean by `ODR-used'?
http://en.cppreference.com/w/cpp/language/definition#One_Definition_Rule
Thanks, this was very helpful