Undefined reference, template struct and constexpr static member - c++

I got some problem with a static constexpr member of a template struct. The code compiles but I get linking error. Here's what I'm trying to do:
template<int n>
struct Test {
static constexpr auto invoke = make_tuple(2, "test", 3.4);
};
template<typename T>
void test(T&& t) {
cout << t << endl;
}
int main() {
test(get<0>(Test<2>::invoke));
return 0;
}
I got linking errors with that, so I tried this:
template<int n>
struct Test {
static constexpr auto invoke = make_tuple(2, "test", 3.4);
};
// declare it outside the class
template<int n>
constexpr decltype(Test<n>::invoke) Test<n>::invoke;
template<typename T>
void test(T&& t) {
cout << t << endl;
}
int main() {
test(get<0>(Test<2>::invoke));
return 0;
}
But instead I got this strange error:
error: redefinition of 'invoke' with a different type: 'const decltype(Test<n>::invoke)' vs 'const std::tuple<int, const char *, double>'
A different type??
Obviously, the non-template version works just fine:
struct Test {
static constexpr auto invoke = make_tuple(2, "test", 3.4);
};
constexpr decltype(Test::invoke) Test::invoke;
template<typename T>
void test(T&& t) {
cout << t << endl;
}
int main() {
test(get<0>(Test::invoke));
return 0;
}
How can I get the template version to work? Thank you very much

It looks like you are running into an interesting corner case with decltype, this is covered in the clang bug report Static constexpr definitions used inside template which has the following example with a similar error as yours:
This compiles fine, but when I make the class A, a template like this:
struct L
{
void operator()() const
{}
};
template<class X>
struct A
{
static constexpr auto F = L();
};
template<class X>
constexpr decltype(A<X>::F) A<X>::F;
int main()
{
A<void>::F();
return 0;
}
Clang crashes, if I change the definition line for F to this:
template<class X>
constexpr typename std::remove_const<decltype(A<X>::F)>::type A<X>::F;
Then clang produces this error:
error: redefinition of 'F' with a different type
constexpr typename std::remove_const<decltype(A<X>::F)>::type A<X>::F;
^
note: previous definition is here
static constexpr auto F = L();
^
and Richard Smith's reply was as follows:
This error is correct. 'constexpr' and 'auto' are red herrings here.
Reduced testcase:
template<class X> struct A { static int F; };
template<class X> decltype(A<X>::F) A<X>::F;
Per C++11 [temp.type]p2, "If an expression e involves a template
parameter, decltype(e) denotes a unique dependent type." Therefore the
type of A::F does not match the type in the template.
the full quote for that from the C++14 draft is as follows:
If an expression e involves a template parameter, decltype(e) denotes
a unique dependent type. Two such decltype-specifiers refer to the
same type only if their expressions are equivalent (14.5.6.1). [ Note:
however, it may be aliased, e.g., by a typedef-name. —end note ]
The only obvious way I can see to get this work is:
template<int n>
constexpr decltype(make_tuple(2, "test", 3.4)) Test<n>::invoke;
No work around was offered in the bug report.

How can I get the template version to work?
FWIW, your method works fine on my desktop, which uses g++ 4.8.4.
You can use:
template<int n>
constexpr decltype(make_tuple(2, "test", 3.4)) Test<n>::invoke;
The following also works on my desktop:
template<int n>
struct Test {
static constexpr auto invoke = make_tuple(2, "test", 3.4);
typedef decltype(invoke) invoke_t;
};
template<int n>
constexpr typename Test<n>::invoke_t Test<n>::invoke;

Related

When overloading a function with multiple inheritance, GCC says calling it is ambiguous, but Clang and MSVC do not

I am using this variant library: https://github.com/cbeck88/strict-variant. It provides a class similar to std::variant and boost::variant. Given this struct:
struct S
{
explicit S(double) {}
};
I want to do this:
strict_variant::variant<double, S> v = 2.0;
This works with Clang 5.0.1 and MSVC 19.12.25831.00, but fails to compile with GCC 7.2.1.
I looked at the library's code and reduced the problem to this:
#include <iostream>
struct S
{
constexpr S() {}
constexpr explicit S(double) {}
};
template<unsigned i> struct init_helper;
template<> struct init_helper<0> { using type = double; };
template<> struct init_helper<1> { using type = S; };
template<unsigned i>
struct initializer_leaf
{
using target_type = typename init_helper<i>::type;
constexpr unsigned operator()(target_type) const
{
return i;
}
};
struct initializer : initializer_leaf<0>, initializer_leaf<1>
{
};
int main()
{
std::cout << initializer()(double{}) << " = double" << '\n';
std::cout << initializer()(S{}) << " = S" << '\n';
return 0;
}
with the output being
0 = double
1 = S
GCC says:
strict_variant_test.cpp: In function ‘int main()’:
strict_variant_test.cpp:29:37: error: request for member ‘operator()’ is ambiguous
std::cout << initializer()(double{}) << " = double" << '\n';
^
strict_variant_test.cpp:17:21: note: candidates are: constexpr unsigned int initializer_leaf<i>::operator()(initializer_leaf<i>::target_type) const [with unsigned int i = 1; initializer_leaf<i>::target_type = S]
constexpr unsigned operator()(target_type) const
^~~~~~~~
strict_variant_test.cpp:17:21: note: constexpr unsigned int initializer_leaf<i>::operator()(initializer_leaf<i>::target_type) const [with unsigned int i = 0; initializer_leaf<i>::target_type = double]
strict_variant_test.cpp:30:32: error: request for member ‘operator()’ is ambiguous
std::cout << initializer()(S{}) << " = S" << '\n';
^
strict_variant_test.cpp:17:21: note: candidates are: constexpr unsigned int initializer_leaf<i>::operator()(initializer_leaf<i>::target_type) const [with unsigned int i = 1; initializer_leaf<i>::target_type = S]
constexpr unsigned operator()(target_type) const
^~~~~~~~
strict_variant_test.cpp:17:21: note: constexpr unsigned int initializer_leaf<i>::operator()(initializer_leaf<i>::target_type) const [with unsigned int i = 0; initializer_leaf<i>::target_type = double]
But, it works with GCC (and still Clang and MSVC) when I change the definition of initializer to this:
struct initializer
{
constexpr unsigned operator()(double) const
{
return 0;
}
constexpr unsigned operator()(S) const
{
return 1;
}
};
My understanding of C++ says that this is equivalent, so I assume that this is a bug in GCC, but I have often run into problems where the standard says surprising things and my assumption is wrong. So, my question is: whose fault is this? Does GCC have a bug, do Clang and MSVC have a bug, or is the interpretation of the code undefined/unspecified such that all compilers are right? If the code is wrong, how can it be fixed?
This is actually a clang bug.
The rule of thumb is that the names in different scopes don't overload. Here's a reduced example:
template <typename T>
class Base {
public:
void foo(T ) { }
};
template <typename... Ts>
struct Derived: Base<Ts>...
{};
int main()
{
Derived<int, double>().foo(0); // error
}
This should be an error because the class member lookup rules state that basically only one base class can contain a given name. If more than one base class has the same name, lookup is ambiguous. The resolution here is to bring both base class names into the derived class with a using-declaration. In C++17, that using declaration can be a pack expansion, which makes this problem a whole lot easier:
template <typename T>
class Base {
public:
void foo(T ) { }
};
template <typename... Ts>
struct Derived: Base<Ts>...
{
using Base<Ts>::foo...;
};
int main()
{
Derived<int, double>().foo(0); // ok! calls Base<int>::foo
}
For the specific library, this code:
template <typename T, unsigned... us>
struct initializer_base<T, mpl::ulist<us...>> : initializer_leaf<T, us>... {
static_assert(sizeof...(us) > 0, "All value types were inelligible!");
};
should look like:
template <typename T, unsigned... us>
struct initializer_base<T, mpl::ulist<us...>> : initializer_leaf<T, us>... {
static_assert(sizeof...(us) > 0, "All value types were inelligible!");
using initializer_leaf<T, us>::operator()...; // (*) <==
};
(although I guess the library is targeting C++11, so I submitted a C++11-compliant fix for it... which is just a bit more verbose).

using result of constexpr function as a template parameter (clang vs gcc)

Please take a look at the code below, sorry that is a bit lengthy, but I did my best to reproduce the problem with a minimum example (there is also a live copy of it). There I basically have a metafunction which returns the size of string literal, and constexpr function which wraps it. Then when I call those functions in a template parameter gcc (5.4, 6.2) is happy with it, but clang (3.8, 3.9) barfs with "non-type template argument is not a constant expression" in test body on strsize(s). If I replace with a str_size<S> both compilers are happy. So the questions are:
whether that is a problem with clang, or my code?
What is the way to make it compile on both clang and gcc with constexpr function?
template<size_t N> using string_literal_t = char[N];
template<class T> struct StrSize; ///< metafunction to get the size of string literal alikes
/// specialize StrSize for string literals
template<size_t N>
struct StrSize <string_literal_t<N>>{ static constexpr size_t value = N-1; };
/// template variable, just for convenience
template <class T>
constexpr size_t str_size = StrSize<T>::value;
/// now do the same but with constexpr function
template<class T>
constexpr auto strsize(const T&) noexcept-> decltype(str_size<T>) {
return str_size<T>;
}
template<class S, size_t... Is>
constexpr auto test_helper(const S& s, index_sequence<Is...>) noexcept-> array<char, str_size<S>> {
return {s[Is]...};
}
template<class S>
constexpr auto test(const S& s) noexcept-> decltype(auto) {
// return test_helper(s, make_index_sequence<str_size<S>>{}); // this work in both clang and gcc
return test_helper(s, make_index_sequence<strsize(s)>{}); // this works only in gcc
}
auto main(int argc, char *argv[])-> int {
static_assert(strsize("qwe") == 3, "");
static_assert(noexcept(test("qwe")) == true, "");
return 0;
}
Clang is correct here. The problem is in the code and in GCC, which erroneously accepted it. This was fixed in GCC 10: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66477
According to the standard expr.const#5.12:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine, would evaluate one of the following:
...
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
it is usable in constant expressions or
its lifetime began within the evaluation of E;
And here the compiler is unable to verify the validity of reference in test(const S& s).
Actually there is a nice article to read here: https://brevzin.github.io/c++/2020/02/05/constexpr-array-size/
As to your other question:
What is the way to make it compile on both clang and gcc with constexpr function?
You can replace references with std::array passed by value:
#include <array>
using namespace std;
template<class T> struct StrSize;
template<size_t N>
struct StrSize <array<char,N>>{ static constexpr size_t value = N-1; };
template <class T>
constexpr size_t str_size = StrSize<T>::value;
template<class T>
constexpr auto strsize(const T&) noexcept-> decltype(str_size<T>) {
return str_size<T>;
}
template<class S, size_t... Is>
constexpr auto test_helper(const S& s, index_sequence<Is...>) noexcept-> array<char, str_size<S>> {
return {s[Is]...};
}
constexpr auto test(array<char,4> s) noexcept-> decltype(auto) {
return test_helper(s, make_index_sequence<strsize(s)>{});
}
int main() {
static_assert(noexcept(test({"qwe"})) == true, "");
}
Demo: https://gcc.godbolt.org/z/G8zof38b1

Static constants of templated class

I have been playing around with template classes and constants in gcc 4.8.2 and came across an interesting linker error:
#include <iostream>
using namespace std;
template <class T, int m>
class A {
public:
static const T param = m;
T value;
A(const T &value, const T &dummy = T()) : value(value) {}
};
// Works with this
// template <class T, int m>
// const T A<T, m>::param = m;
template <class T, int m>
A<T, m> operator +(const A<T, m> &a, const A<T, m> &b) {
if (a.param != b.param) exit(1);
// Works if I replace a.param with 0
return A<T, m>(a.value + b.value, a.param);
}
int main() {
A<int, 2> v = A<int, 2>(1) + A<int, 2>(2);
cout << v.value << endl;
return 0;
}
Compiling the code in the current state gives a linker error, telling me that A::param is not defined.
Trying this code in Visual Studio 2008 as well, it compiles and links without any problems. On gcc, it compiles when I either use the external declaration of the param constant, or if I replace a.param with 0 or nothing on the line indicated.
What I do not understand is why the line containing the if statement can use a.param without compilation error, while I cannot pass a.param to the constructor without declaring it externally.
So my question is: When do I need to declare param externally, and what is the difference between the access in the if statement and the constructor call? Which of the two compilers I tested does the "right" thing here, and which one has extended the standard?
Playing around a little more, I realized it also works when I specify the -O2 flag to g++.
template <class T, int m>
class A {
public:
static const T param = m;
T value;
A(const T &value, const T &dummy = T()) : value(value) {}
};
// Works with this
// template <class T, int m>
// const T A<T, m>::param = m;
//gotta define it here!
template <class T, int m>
const T A<T, m>::param;
template <class T, int m>
A<T, m> operator +(const A<T, m> &a, const A<T, m> &b) {
if (a.param != b.param) exit(1);
// Works if I replace a.param with 0
return A<T, m>(a.value + b.value, a.param);
}
int main() {
A<int, 2> v = A<int, 2>(1) + A<int, 2>(2);
std::cout << v.value << std::endl;
return 0;
}
from here
If a static data member is of const integral or const enumeration type, you may specify a constant initializer in the static data member's declaration. This constant initializer must be an integral constant expression. Note that the constant initializer is not a definition. You still need to define the static member in an enclosing namespace.
In the cases that is working, the compilers are being non-compliant. Microsoft is non-compliant cuz Microsoft is almost never compliant, the g++ with -O2 is kinda funny, but in either case you've got no definition!
Edit:
As a rule of thumb, I always define my static constant member variables outside of the class because then I never have to remember it. In this case, class A would be invalid if T was anything other than an integral type, such as a float. So rather than do any patterns around the exception, I tend to just stick with my patterns around the rule.

SFINAE to Selectively Include Member

I am trying to write a template class which may or may not define a particular member function depending on its template parameter type. Further the return type of this member function depends on the return type of of a member of the template paramter (if defined).
Below is a minimal example of my code
#include <iostream>
#include <type_traits>
template <typename T>
struct has_foo_int {
private:
template <typename U>
static decltype(std::declval<U>().foo(0), void(), std::true_type()) test(int);
template <typename>
static std::false_type test(...);
public:
typedef decltype(test<T>(0)) test_type;
enum { value = test_type::value };
};
template <typename T, bool HasFooInt>
struct foo_int_return_type;
template<typename T>
struct foo_int_return_type<T,false> {};
template<typename T>
struct foo_int_return_type<T,true> {
using type = decltype(std::declval<T>().foo(0));
};
template<typename T>
struct mystruct
{
T val;
//auto someMethod(int i) -> decltype(std::declval<T>().foo(0)) // error: request for member ‘foo’ in ‘std::declval<double>()’, which is of non-class type ‘double’
//auto someMethod(int i) -> typename foo_int_return_type<T,has_foo_int<T>::value>::type // error: no type named ‘type’ in ‘struct foo_int_return_type<double, false>’
template<typename R=typename foo_int_return_type<T,has_foo_int<T>::value>::type> R someMethod(int i) // error: no type named ‘type’ in ‘struct foo_int_return_type<double, false>’
{
return val.foo(i);
}
};
struct with_foo_int {
int foo(int i){
return i+1;
}
};
using namespace std;
int main(void)
{
mystruct<with_foo_int> ms1;
cout << ms1.someMethod(41) << endl;
mystruct<double> ms2;
return 0;
}
What I would like to happen is that the code compiles fine and outputs 42 for ms1.someFunc(41). I would also expect that if one accidentally tried to call someFunc on ms2 that it would fail to compile.
Unfortunately each of the alternatives I have tried has failed. The first and second, I think I understand why they wouldn't work.
I read here that SFINAE only works for template functions so I tried giving a dummy template parameter to work out the return type but this too fails in the same way.
I'm clearly not understanding something here, what am I missing? Is it possible to achieve what I'm trying to do?
Thanks.
P.s. I'm using g++ 4.7.3
P.p.s I have also tried std::enable_if but get much the same results as with my foo_int_return_type struct.
Here is a short, tidy and documented way of doing what you are attempting,
with some possible bugs addressed thereafter.
#include <type_traits>
/*
Template `has_mf_foo_accepts_int_returns_int<T>`
has a static boolean public member `value` that == true
if and only if `T` is a class type that has a public
member function or member function overload
`int T::foo(ArgType) [const]` where `ArgType`
is a type to which `int` is implicitly convertible.
*/
template <typename T>
struct has_mf_foo_accepts_int_returns_int {
/* SFINAE success:
We know now here `int *` is convertible to
"pointer to return-type of T::foo(0)"
*/
template<typename A>
static constexpr bool test(
decltype(std::declval<A>().foo(0)) *prt) {
/* Yes, but is the return-type of `T::foo(0)`
actually *the same* as `int`?...
*/
return std::is_same<int *,decltype(prt)>::value;
}
// SFINAE failure :(
template <typename A>
static constexpr bool test(...) {
return false;
}
/* SFINAE probe.
Can we convert `(int *)nullptr to
"pointer to the return type of T::foo(0)"?
*/
static const bool value = test<T>(static_cast<int *>(nullptr));
};
template<typename T>
struct mystruct
{
using has_good_foo = has_mf_foo_accepts_int_returns_int<T>;
T val;
/* SFINAE:
`template<typename R> R someMethod(R)` will be this if and only
if `R` == `int` and `has_good_foo` == true.
*/
template<typename R = int>
typename std::enable_if<
(has_good_foo::value && std::is_same<R,int>::value),R
>::type
someMethod(R i) {
return val.foo(i);
}
/* SFINAE:
`template<typename R> R someMethod(R)` will be this if and only
if `R` != `int` or `has_good_foo` != true.
*/
template<typename R = int>
typename std::enable_if<
!(has_good_foo::value && std::is_same<R,int>::value),R
>::type
someMethod(R i) {
static_assert(has_good_foo::value && std::is_same<R,int>::value,
"mystruct<T> does not implement someMethod(R)");
return i;
}
};
// Testing...
#include <iostream>
struct with_foo_int
{
int foo(int i) {
return i + 1;
}
};
using namespace std;
int main(void)
{
mystruct<with_foo_int> ms1;
cout << ms1.someMethod(41) << endl;
mystruct<double> ms2;
cout << ms2.someMethod(41) << endl; // static_assert failure
return 0;
}
This solution faithfully reproduces a couple of possible loopholes in your
own attempt as posted:-
1) It looks as if you may believe that evaluating std::declval<U>().foo(0) is
a SFINAE way of determining whether U::foo exists and takes a single argument
of type int. It doesn't. It is merely a SFINAE way of determining whether
U::foo(ArgType) exists where ArgType is anything to which 0 is
implicitly convertible. Thus ArgType could be any pointer-or-arithmetic
type, not just int.
2) You may not have considered that std::declval<U>().foo(0) will be satisfied
if either or both of U::foo(ArgType) U::foo(ArgType) const exists. You
may well care whether you call a const or a non-const member function on
U, and you would certainly care which of two member function you call. If
with_foo_int were defined as:
struct with_foo_int
{
int foo(int i) const {
return i + 1;
}
int foo(int i) {
return i + 2;
}
};
then the solution given would call the non-const overload and
ms1.someMethod(41) would == 43.
2) Is easily dealt with. If you wish to ensure that you can only call
T::foo(ArgType) const then add a const qualifier to mystruct::someMethod.
If you don't care or wish only to call T::foo(ArgType) then leave things
as they are.
1) is a little harder to solve, because you must craft a SNIFAE probe for
T::foo that is satisfied only if it has the right signature, and that
signature will either be const qualified or not. Let's assume you want
int T::foo(int) const. In that case, replace template
has_mf_foo_accepts_int_returns_int with:
/* Template `has_mf_foo_arg_int_returns_int<T>
has a static boolean public member `value` that == true
if and only if `T` is a class type that has an un-overloaded
a public member `int T::foo(int) const`.
*/
template< typename T>
struct has_mf_foo_arg_int_returns_int
{
/* SFINAE foo-has-correct-sig :) */
template<typename A>
static std::true_type test(int (A::*)(int) const) {
return std::true_type();
}
/* SFINAE foo-exists :) */
template <typename A>
static decltype(test(&A::foo))
test(decltype(&A::foo),void *) {
/* foo exists. What about sig? */
typedef decltype(test(&A::foo)) return_type;
return return_type();
}
/* SFINAE game over :( */
template<typename A>
static std::false_type test(...) {
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test<T>(0,0)) type;
static const bool value = type::value; /* Which is it? */
};
and in template mystruct replace:
using has_good_foo = has_mf_foo_accepts_int_returns_int<T>;
with:
using has_good_foo = has_mf_foo_arg_int_returns_int<T>;
(Template has_mf_foo_arg_int_returns_int is adapted
from my other answer and
you can read how it works there.)
What you gain in SFINAE-precision from the latter approach comes at
a price. The approach requires you to attempt to take the address of T::foo,
to see if it exists. But C++ will not give you the address of an overloaded
member function, so this approach will fail if T::foo is overloaded.
The code here will compile (or appropriately static_assert) with
GCC >= 4.7.2 clang >= 3.2.

Substitution failure sometimes is an error

It seems that substitution failure sometimes is an error.
Could someone tell me when it will be an error and when it will not?
See run result here
Thank you!
Thank you for the informative answer!
This code doens't compile with g++ 4.8, but works as expected with clang++ 3.2, g++ 4.7.3 and intel 13.0.1.
So now I'm sure this is a bug to g++ 4.8. I have reported this to gcc bugzila.
In substitution of ‘template<class C> static constexpr int has<T>::test(decltype (sizeof (C:: x))) [with C = C; T = foo] [with C = foo]’:
required from ‘const int has<foo>::value’
required from here
error: invalid use of non-static member function ‘std::string foo::x()’
The code
template <typename T>
struct has {
template <typename>
constexpr static int test(...) {
return 0;
}
template <typename C>
constexpr static int test(decltype(sizeof(C::x))) { // Doesn't compile.
return 1; // Is a member variable.
}
template <typename C, int c =
sizeof(decltype(((C*)nullptr)->x()))>
constexpr static int test(int) {
return 2; // Is a member function.
}
static const int value = test<T>(0);
};
struct foo {
string x();
};
struct bar {
string x;
};
int main() {
std::cout << has<int>::value << std::endl;
std::cout << has<foo>::value << std::endl;
std::cout << has<bar>::value << std::endl;
}
The rule is essentially that the error must depend on a template parameter of the declaration which it immediately belongs to.
This example looks like a compiler bug. It doesn't correctly not-an-error out the particular syntax flaw.
If you want it to return success when the class has a nonstatic member function named x, you should use &C::x, because you're allowed to take a pointer to a member function but not simply to name it as a standalone subexpression.
My preferred way to do this would be
template< typename, typename = void >
struct has_x
: std::false_type {};
template< typename t >
struct has_x< t, typename std::enable_if< & t::x == & t::x >::type >
: std::true_type {};