Undefined reference only when using clang

SOLVED

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

15
17b29a•

It's being ODR-used, so you need to provide an out-of-line definition, like:

template<int d_pow, int m_pow, int t_pow, const double* d_scale,
         const double* m_scale, const double* t_scale>
constexpr double Unit<d_pow, m_pow, t_pow, d_scale, m_scale, t_scale>::scale;
2
MerionesOP•

What do you mean by `ODR-used'?

1
17b29a•
2
MerionesOP•

Thanks, this was very helpful

1
specialpatrol•

I don't think you can use a variable to instantiate the template - how does it know at compile time how to make it. Because you hardcoded the variable g++ must have deduced it and compiled it, but clang kinda rightly refused.

2