Take the following snippet
#include <iostream>
using namespace std;
template<int i, typename T = int> struct A
{
T num = i;
A<i, T>()
{
cout << "Instantiated a A<" << i << ">" << endl;
}
};
template<int i, int i2> struct B
{
static A<i> a;
static A<i * i2> a2;
};
template<int i, int i2> A<i> B<i, i2>::a{};
template<int i, int i2> A<i * i2> B<i, i2>::a2{};
template<typename T> struct C
{
static void doSomething()
{
cout << "Have a A<" << T::a.num << "> and a A<" << T::a2.num << "> in C" << endl;
}
};
int main() {
typedef C<B<2, 2>> c;
cout << "Typedefined a C\nCalling static member function to initialize C<B<2, 2>>'s B<2, 2>'s A<>s" << endl;
c::doSomething();
return 0;
}
Now with gcc, then this compiles (both C++11 and C++14) and instantiates a and a2 as expected.
Thanks to WhozCraig, this also compiles with clang.
However with Visual C++ (2015 edition), I get a parse error.
main.cpp(37) error C2143: syntax error: missing ';' before '<end Parse>'
Followed by some notes
main.cpp(19): note: while compiling class template static data member 'A<2,int> B<2,2>::a'
main.cpp(26): note: see reference to class template instantiation 'B<2,2>' being compiled
main.cpp(25): note: while compiling class template member function 'void C<B<2,2>>::doSomething(void)'
main.cpp(33): note: see reference to function template instantiation 'void C<B<2,2>>::doSomething(void)' being compiled
main.cpp(33): note: see reference to class template instantiation 'C<B<2,2>>' being compiled
What is going on here?
I was able to reduce the test case, identify the problem (which appears to be a bug in the Visual C++ compiler) and find a workaround:
#include <iostream>
using namespace std;
template<int i> struct A
{
int num = i;
A() {}
};
template<int i> struct B
{
static A<i> a;
};
// MSVC doesn't like this syntax
// |||
// vvv
template<int i> A<i> B<i>::a{};
// To fix the error, rewrite the above line in one of the below ways:
//
// template<int i> A<i> B<i>::a;
// template<int i> A<i> B<i>::a = {};
int main() {
typedef B<2> B2;
cout << B2::a.num << endl;
return 0;
}
Related
I have two enums
#include <iostream>
enum class E1 : unsigned int
{
E11 = 1
};
enum class E2 : unsigned int
{
E21 = 1
};
which have identical underlying values (1) in this case. Next, I have a class C which has two template parameters, an integer j and a value i with type auto.
template<int j, auto i>
struct C
{ C() { std::cout << "none\n"; } };
I want to partially specialize this class for both E1::E11 and E2::E21 like this:
template<int j>
struct C<j, E1::E11>
{ C() { std::cout << j << "E11\n"; } };
template<int j>
struct C<j, E2::E21>
{ C() { std::cout << j << "E21\n"; } };
And for completeness sake here is main that instantiates two objects:
int main()
{
C<0,E1::E11> e11;
C<1,E2::E21> e21;
return 0;
}
The code above works absolutely fine on gcc and icc as can be verified on Godbolt (full code)
But it fails with any other compiler (clang, msvc). With the following message
<source>:27:18: error: ambiguous partial specializations of 'C<0, E1::E11>'
C<0,E1::E11> e11;
^
<source>:18:8: note: partial specialization matches [with j = 0]
struct C<j, E1::E11>
^
<source>:22:8: note: partial specialization matches [with j = 0]
struct C<j, E2::E21>
It's kind of clear to me why this happens. The question that I can't seem to answer though is whether it is possible to solve this in a standard compatible way (if this is some gcc or icc feature) or if there's a workaround for the failing compilers (clang, msvc).
Btw. if I remove the int j template argument and just leave the auto i the code compiles with all compilers.
Thanks in advance,
Arno
The auto template argument can be replaced by
template<int j,typename T,T i>
struct C
{ C() { std::cout << "none\n"; } };
If you are fine with more typing you can explicitly specify the type of the enum:
#include <iostream>
enum class E1 : unsigned int
{
E11 = 1
};
enum class E2 : unsigned int
{
E21 = 1
};
template<int j,typename T,T i>
struct C
{ C() { std::cout << "none\n"; } };
template<int j>
struct C<j,E1, E1::E11>
{ C() { std::cout << j << "E11\n"; } };
template<int j>
struct C<j,E2, E2::E21>
{ C() { std::cout << j << "E21\n"; } };
int main()
{
C<0,E1,E1::E11> e11;
C<1,E2,E2::E21> e21;
return 0;
}
And for less typing you can use a helper function:
template <int j,auto i>
auto make_C(){
return C<j,decltype(i),i>();
}
int main()
{
auto e11 = make_C<0,E1::E11>();
auto e21 = make_C<1,E2::E21>();
}
... or a type trait:
template <int j,auto i>
struct C_helper {
using type = C<j,decltype(i),i>;
};
int main()
{
C_helper<0,E1::E11>::type e11;
C_helper<1,E2::E21>::type e21;
}
msvc/clang seems to have issue with partial specialization in your cases :(
As workaround, you might split the parameter in 2 classes:
template <auto i>
struct EC
{
template <int j>
struct C
{
C() { std::cout << "none\n"; }
};
};
template <>
struct EC<E1::E11>
{
template <int j>
struct C
{
C() { std::cout << j << "E11\n"; }
};
};
// Same for E2::E21
and then
EC<E1::E11>::C<0> e11;
EC<E2::E21>::C<0> e21;
Demo
I need some advice.
On VS2017 (MSVC 14.16.27023) and VS2019 (MSVC 14.27.29110) with c++17 the following code fails:
#include <cstdint>
#include <iostream>
template<int A>
struct Bar {};
template<typename T>
struct Foo
{
static void f() { std::cout << "FOO" << std::endl; }
};
template<template<int...> class C, int... Args>
struct Foo<C<Args...>>
{
static void f() { std::cout << "FOO int spec" << std::endl; }
};
template<template<long...> class C, long... Args>
struct Foo<C<Args...>>
{
static void f() { std::cout << "FOO long spec" << std::endl; }
};
int main(void)
{
Foo<Bar<1>> foo;
foo.f();
}
with the following error:
'Foo<Bar<1>>': more than one partial specialization matches the template argument list
note: could be 'Foo<C<Args...>>'
note: or 'Foo<C<Args...>>'
(the "note"s are pointing to the line Foo<Bar<1> foo;)
As I see it it should be able to determine which specialization to use. Also, on VS2015 (MSVC 14.0.23107.0) it does compiles and works correctly.
The interesting part is that if Bar has 2 template parameters it works by matching to the primary template (the first one).
template<int A, int B>
struct Bar {};
However if the second template parameter has default value it doesn't compile again.
template<int A, int B = 1>
struct Bar {};
Is there a way to make this work on VS2017/2019, or should I file a bug report on VS forum?
I am studying the following post access private member using template trick.
I am wondering how the code should be modified in order to access more than one private varible
I tried the following
#pragma once
template<typename Tag, typename Tag::type M>
struct Rob {
friend typename Tag::type get( typename Tag::type) {
return M;
}
};
// use
struct A {
A(int a) :a(a) { }
private:
int a;
int b;
};
// tag used to access A::a
template<typename Tag, typename Member>
struct TagBase {
typedef Member type;
friend type get(Tag);
};
struct A_f : TagBase<A_f, int A::*> { };
template struct Rob<A_f, &A::a>;
template struct Rob<A_f, &A::b>;
int main() {
A a(42);
std::cout << "proof: " << a.*get(A_f()) << std::endl;
}
But I get the following error
error C2084: function 'int A::* Rob<A_f,pointer-to-member(0x0)>::get(int A::* )' already has a body
message : see previous definition of 'get'
message : see reference to class template instantiation 'Rob<A_f,pointer-to-member(0x4)>' being compiled
This the link to the demo
This is because typename Tag::type is both int A::*, so both instantiation define the same function.
To fix this, you'll need to change the example a bit so it uses multiple tag types:
#include <iostream>
template<typename Tag, typename Tag::type M>
struct Rob {
// Here we receive tag directly
friend typename Tag::type get(Tag) {
return M;
}
};
// use
struct A {
A(int a) :a(a) { }
private:
int a;
int b;
};
// tag used to access A::a
template<typename Tag, typename Member>
struct TagBase {
typedef Member type;
friend type get(Tag);
};
struct A_af : TagBase<A_af, int A::*> { };
struct A_bf : TagBase<A_bf, int A::*> { };
template struct Rob<A_af, &A::a>;
template struct Rob<A_bf, &A::b>;
int main() {
A a(42);
std::cout << "proof: " << a.*get(A_bf()) << a.*get(A_af()) << std::endl;
}
What are the rules for template instantiation when we pass a (multi)derived class to a template function expecting base class? For example:
#include <iostream>
template <int x>
struct C {};
struct D : C<0>, C<1> {};
template <int x>
void f (const C<x> &y) { std::cout << x << "\n"; }
int main ()
{
f (D ());
}
MSVC 2015 prints 0, clang 3.8 - 1 and gcc 6.2 gives compiler error (Demo). And even if you SFINAE-away all overloads except one, the result will still be different:
#include <iostream>
template <int x> struct C {};
template<>
struct C<0> { using type = void; };
struct D : C<0>, C<1> {};
template <int x, typename = typename C<x>::type>
void f (const C<x> &y) { std::cout << x << "\n"; }
int main ()
{
f (D ());
}
Now it compiles only with MSVC, and if you swap C<0> and C<1> only clang will compile it. The problem is that MSVC only tries to instantiate first base, clang - last and gcc prints error too early. Which compiler is right?
gcc 5.4:
/tmp/gcc-explorer-compiler11685-58-1h67lnf/example.cpp: In function 'int main()':
13 : error: no matching function for call to 'f(D)'
f (D ());
^
9 : note: candidate: template<int x> void f(const C<x>&)
void f (const C<x> &y) { std::cout << x << "\n"; }
^
9 : note: template argument deduction/substitution failed:
13 : note: 'const C<x>' is an ambiguous base class of 'D'
f (D ());
^
Compilation failed
Which seems to me to be the correct result, since C<0> and C<1> are equally specialised.
Same result for gcc 6.2
clang 3.8.1 compiles it, which in my view is a compiler bug.
update:
I don't know the actual use case but I was wonder whether this might work for you:
#include <utility>
#include <iostream>
template<class T>
struct has_type
{
template<class U> static auto test(U*) -> decltype(typename U::type{}, std::true_type());
static auto test(...) -> decltype(std::false_type());
using type = decltype(test((T*)0));
static const auto value = type::value;
};
template <int x> struct C {};
template<>
struct C<0> { using type = int; };
template<int...xs>
struct enumerates_C : C<xs>...
{
};
struct D : enumerates_C<0, 1> {};
template<int x, std::enable_if_t<has_type<C<x>>::value>* = nullptr>
void f_impl(const C<x>& y)
{
std::cout << x << "\n";
}
template<int x, std::enable_if_t<not has_type<C<x>>::value>* = nullptr>
void f_impl(const C<x>& y)
{
// do nothing
}
template <int...xs>
void f (const enumerates_C<xs...> &y)
{
using expand = int[];
void(expand { 0,
(f_impl(static_cast<C<xs> const &>(y)),0)...
});
}
int main ()
{
f (D ());
}
expected output (tested on apple clang):
0
What is wrong with the following piece of code?
template<typename X>
struct A {
template<int N>
int foo() const {
return N;
}
};
template<typename X>
struct B {
int bar(const A<X>& v) {
return v.foo<13>();
}
};
#include <iostream>
using std::cout;
using std::endl;
int main() {
A<double> a;
B<double> b;
cout << b.bar(a) << endl;
return 0;
}
Inside the function B::bar the compiler complains:
error: invalid operands of types
‘’ and ‘int’ to binary ‘operator<’
If A is not a template, everything compiles fine.
Change return v.foo<13>(); to return v.template foo<13>(); because foo is a dependent name and you need to mention that explicitly using .template construct.