I try to define a static variable outside the class scope like:
template<typename T>
struct Foo {
void set(int i) {
}
static constexpr decltype(&Foo<T>::set) i = &Foo<T>::set;
};
template<typename T>
constexpr decltype(&Foo<T>::set) Foo<T>::i;
Live example.
But I get following error (for all gcc >= 4.7):
conflicting declaration 'constexpr decltype (& Foo<T>::set(int)) Foo<T>::i'
note: previous declaration as 'constexpr decltype (& Foo<T>::set(int)) Foo<T>::i'
All clang version (clang >= 3.2) do not have any problem with my code.
The problem seems to be the function reference. It works without using a template class.
My questions:
Is it a bug?
How to do it in gcc?
I don't know if it's a bug or not, but you can do it like this:
template<typename T>
struct Foo {
void set(int i) {
}
typedef decltype(&Foo<T>::set) function_type;
static constexpr function_type i = &Foo<T>::set;
};
template<typename T>
constexpr typename Foo<T>::function_type Foo<T>::i;
int main()
{
Foo<int> f;
}
Related
I am working maintenance on a program with a bunch of different structures that are fundamentally similar. I want to write a template method that utilizes SFINAE to enable calling this method from the client. When I define the template specialization inline, everything works as expected. However, when I try to move the template definition into a separate compilation unit, I run into issues. I am trying to move the template implementation into a separate file in order to enable using forward declarations for most of the dependent classes. The following is an example of what I am attempting to achieve:
// Impl.h
#pragma once
struct A {
struct {
int value;
} valueA;
};
struct B {
struct {
int value;
} valueB;
};
template<typename T>
int GetValue(T const &value);
// Impl.cpp
#include "Impl.h"
#include <type_traits>
using std::enable_if_t;
using std::remove_reference_t;
template<typename T, typename U, U(T::*Value)>
static inline int GetValueImpl(T const &value) {
return (value.*Value).value;
}
template<typename T>
enable_if_t<T::valueA, int> GetValue(T const &value) {
static constexpr auto T::*const Value = &T::valueA;
typedef remove_reference_t<decltype(value.*Value)> ValueType;
return GetValueImpl<T, ValueType, Value>(value);
}
template<typename T>
enable_if_t<T::valueB, int> GetValue(T const &value) {
static constexpr auto T::*const Value = &T::valueB;
typedef remove_reference_t<decltype(value.*Value)> ValueType;
return GetValueImpl<T, ValueType, Value>(value);
}
template<> int GetValue(A const &); // C2912 here
template<> int GetValue(B const &); // C2912 here
I am using VS2017u2, and am getting error C2912: explicitt specialization 'int GetValue(const A &)' is not a specialization of a function template. Does anyone know how to make this work with the definitions in a separate compilation unit?
When you write enable_if_t<T::valueA, int>, it is checking for a static member of T called valueA (Which would presumably be a static constexpr bool valueA = /* true or false */;, like what T::value would mean if T was using T = std::is_same<U, V>; ).
To actually check if it has a member called valueA or valueB, put it in a context where there would be a substitution error if the member didn't exist, or true. Something like:
// Pointers to member variables can never be null
// so these will always be enabled if `valueA` or
// `valueB` exist in the first place
enable_if_t<&T::valueA != nullptr, int>
enable_if_t<&T::valueB != nullptr, int>
// But that also allows pointers to static members
// so if you don't want that, you can do something else.
// Like checking if `&T::member` is a pointer to a member
// (But this is probably overkill as you have a specific set
// of types and none of those names are ever static members
// and if you didn't there is a small caveat with
// an overloaded `operator&` but that doesn't really matter)
enable_if_t<std::is_same_v<decltype(&T::valueA), decltype(T::valueA) T::*>, int>
enable_if_t<std::is_same_v<decltype(&T::valueB), decltype(T::valueB) T::*>, int>
Even after fixing the SFINAE check, you are using the wrong syntax for template instantiation. You should use:
template int GetValue(A const &); // No `<>`
template int GetValue(B const &);
And after that, it still doesn't work because template<typename T> enable_if_t<..., int> GetValue(T const&); and template<typename T> int GetValue(T const&); are different functions, so it is ambiguous which one it should instantiate (as both would work). You need to make both of those into different functions (GetValueImpl2 in my example) and have GetValue declared the same way as in the header:
#include <type_traits>
#include "Impl.h"
using std::enable_if_t;
using std::remove_reference_t;
template<typename T, typename U, U(T::*Value)>
static inline int GetValueImpl(T const &value) {
return (value.*Value).value;
}
template<typename T>
enable_if_t<std::is_same_v<decltype(&T::valueA), decltype(T::valueA) T::*>, int> GetValueImpl2(T const &value) {
static constexpr auto T::*const Value = &T::valueA;
using ValueType = decltype(T::valueA);
return GetValueImpl<T, ValueType, Value>(value);
}
template<typename T>
enable_if_t<std::is_same_v<decltype(&T::valueB), decltype(T::valueB) T::*>, int> GetValueImpl2(T const &value) {
static constexpr auto T::*const Value = &T::valueB;
using ValueType = decltype(T::valueB);
return GetValueImpl<T, ValueType, Value>(value);
}
template<typename T>
int GetValue(T const&value) {
return GetValueImpl2(value);
}
template int GetValue<A>(A const &);
template int GetValue<B>(B const &);
I was experimenting with C++ template metaprogramming trying to create a storage class with the following semantics: it takes arbitrary number of types and stores a container of user-defined type for each of them with common access interface. I was able to implement it with following code using multiple inheritance from decltype-expanded list (A and B are just dummy structures to be put into Storage):
#include <iostream>
#include <string>
#include <map>
#include <unordered_map>
struct A
{
int v = -1;
};
struct B
{
std::string v;
};
typedef int Key;
template<typename T>
auto componentContainer();
template<>
auto componentContainer<A>()
{
return std::unordered_map<Key, A>();
}
template<>
auto componentContainer<B>()
{
return std::map<Key, B>();
}
template<typename... Component>
struct Storage : public decltype(componentContainer<Component>())...
{
template <typename T>
using Container = decltype(componentContainer<T>());
template<typename T>
T& get(int index)
{
return Container<T>::operator [](index);
}
template<typename T>
const T& get(int index) const
{
return Container<T>::operator [](index);
}
template<typename T>
void put(int index, const T& v)
{
Container<T>::operator [](index) = v;
}
template<typename T, typename F>
void apply(F f)
{
for (auto p = Container<T>::begin();
p != Container<T>::end();
p++)
{
f(p);
}
}
};
int main(int argc, char** argv)
{
Storage<A,B> s;
s.put<A>(0, {12});
s.put<A>(3, {42});
s.put<B>(0, {"melta"});
s.put<B>(42, {"multimelta"});
auto printer = [](auto p) { std::cout <<p->first <<": " << p->second.v <<std::endl;};
s.apply<A>(printer);
s.apply<B>(printer);
return 0;
}
This code compiles just fine in gcc 5.1.0 and produces an expected result, but fails to compile in Visual Studio 2015 with following error message:
main.cpp(37): error C2143: syntax error: missing ',' before '...'
main.cpp(70): note: see reference to class template instantiation 'Storage<Component...>' being compiled
main.cpp(37): error C3520: 'Component': parameter pack must be expanded in this context
main.cpp(74): note: see reference to class template instantiation 'Storage<A,B>' being compiled
main.cpp(37): error C3770: 'unknown-type': is not a valid base class
The thing is, I'm not sure if it's legal (i.e., standard-compliant) to inherit from expanded decltype list like that. So, my questions are:
Is struct Storage: public decltype(componentContainer<Component>())... a legal thing in standard C++ or is it a gcc feature?
If it is, can in be done in Visual Studio?
This works for me in MSVC.
template<typename T>
struct StorageBase
{
using Type = decltype(componentContainer<T>());
};
template<typename... Component>
struct Storage : public StorageBase<Component>::Type...
{ }
The syntax error leads me to believe that the compiler is trying to evaluate the decltype expression before expanding the parameter pack - thus why it is also emitting 'Component': parameter pack must be expanded in this context.
Simplifying the expression by using StorageBase to do the dirty work with decltype looks to do the job.
Instead of inheritance, you might use composition (thanks to std::tuple):
template <typename T>
using Container = decltype(componentContainer<T>());
template <typename... Components>
class Storage
{
public:
template<typename T>
T& get(int index) { return std::get<Container<T>>(t)[index]; }
template<typename T>
const T& get(int index) const { return std::get<Container<T>>(t).at(index); }
template<typename T>
void put(int index, const T& v) { std::get<Container<T>>(t)[index] = v; }
template<typename T, typename F>
void apply(F f)
{
for (const auto& p : std::get<Container<T>>(t))
{
f(p);
}
}
private:
std::tuple<Container<Components>...> t;
};
We have:
template<typename T>
struct A {
void foo(int a) {
T::foo(a);
}
};
template<typename T>
struct B {
template struct A<T>; // concept check
};
So, I define a concept checker A that checks T by forwarding foo to T::foo.
Now, I want to check whether the argument passed to B satisfies the concept A by explicit instantiation, but the compiler complains that it's the wrong namespace. How can I fix that?
Something like this perhaps:
template<typename T, void(T::*)(int)>
struct A {};
template<typename T>
struct B {
using Check = A<T, &T::foo>;
};
Demo
Or this:
template<typename T>
struct B {
static_assert(
std::is_same<decltype(&T::foo), void(T::*)(int)>::value,
"No T::foo(int) member");
};
So, I found a working example:
#include <tuple>
template<typename A>
struct IA : A {
void foo(int a) {
A::foo(a);
}
void bar(double a) {
A::bar(a);
}
static constexpr auto $ = std::make_tuple(&IA::foo, &IA::bar);
};
template<typename T>
struct B {
// trigger concept/interface check of T "implements" IA
static constexpr auto $ = IA<T>::$;
};
struct Test {
void foo(int a) {}
void bar(int a, int b) {}
};
int main() {
B<Test> b;
b = b;
}
The generation of $ in the structs triggers the compilation. The compiler in the example above correctly complains with:
In instantiation of 'void IA<A>::bar(double) [with A = Test]':
13:57: required from 'constexpr const std::tuple<void (IA<Test>::*)(int), void (IA<Test>::*)(double)> IA<Test>::$'
18:27: recursively required from 'constexpr const std::tuple<void (IA<Test>::*)(int), void (IA<Test>::*)(double)> B<Test>::$'
18:27: required from 'struct B<Test>'
28:13: required from here
10:17: error: no matching function for call to 'IA<Test>::bar(double&)'
10:17: note: candidate is:
24:10: note: void Test::bar(int, int)
24:10: note: candidate expects 2 arguments, 1 provided
Why forward declaration as follows :
template<typename T> struct std::hash;
fails to compile with gcc and clang, but compiles with Visual Studio 2015?
gcc 6.1.0 (using coliru):
main.cpp:11:34: error: invalid use of template-name 'std::hash' without an argument list
template<typename T> struct std::hash;
^~~~
clang 3.8.0 (using coliru):
main.cpp:11:29: error: forward declaration of struct cannot have a nested name specifier
template<typename T> struct std::hash;
^~~~~
it works under VS (http://webcompiler.cloudapp.net/). Which compiler is right?
btw. the same declaration is used in C++ Primer 5th edition. Well - nearly the same it uses class instead of struct: template <class T> class std::hash; which is wrong.
full code:
#include <unordered_map>
/*
// compiles with gcc,clang,VS
namespace std {
template<typename T>
struct hash;
}*/
// Compiles only with VS
template<typename T> struct std::hash;
struct MyData {
MyData() {}
MyData(int d1, int d2) : data1(d1), data2(d2) {}
bool operator==(const MyData& rop) const {
return rop.data1 == data1 && rop.data2 == data2;
}
friend struct std::hash<MyData>;
private:
int data1;
int data2;
};
namespace std {
template<>
struct hash<MyData> {
typedef MyData argument_type;
typedef size_t result_type;
size_t operator()(const argument_type& data) const noexcept;
};
size_t hash<MyData>::operator()(const argument_type& data) const noexcept {
return hash<unsigned>()(data.data1) ^ hash<unsigned>()(data.data2);
}
}
int main() {
std::unordered_map<MyData, std::string> mm;
mm[MyData(1,1)] = "test1";
mm[MyData(2,2)] = "test1";
}
The reason, seems to be largely because a forward declaration has to function much like a regular declaration. i.e. encased in a namespace, not prefixed by one. I guess this would allow the same parser to be used for declarations and forward declarations which makes sense.
I'm seeing a strange error from Clang (3.4 and 3.5) when I try to compile this C++14 code that uses a variable template of lambda type.
Here's the C++14 code as I'd like to write it.
Here's a second version, with some of the library _t and _v stuff removed to make the compiler happy:
#include <type_traits>
template <class T>
class forward_if_wrapper {
template <class U, class Enable = typename std::enable_if<std::is_lvalue_reference<U>::value>::type>
static U forward(U&& u) {
return u;
}
template <class U, class Enable = typename std::enable_if<!std::is_lvalue_reference<U>::value>::type>
static U&& forward(U&& t) {
return static_cast<U&&>(t);
}
};
auto forward = [](auto&& t) { return forward_if_wrapper<decltype(t)>::forward(t); };
template <class T> auto forward_if = [](auto&& u) { return forward_if_wrapper<T>::forward(u); };
// --------
#include <stdio.h>
#include <vector>
template<class Elt>
void bar(Elt&& e) {
printf("Called %s\n", __PRETTY_FUNCTION__);
}
template<class Container>
void foo(Container&& c) {
for (auto&& elt : c) {
bar(forward_if<Container>(elt));
}
}
int main() {
std::vector<int> v = {1,2};
foo(v);
foo(std::move(v));
}
The error I see from the second code with Clang is:
test.cc:34:13: error: called object type 'auto' is not a function or function pointer
bar(forward_if<Container>(elt));
^~~~~~~~~~~~~~~~~~~~~~~
test.cc:43:5: note: in instantiation of function template specialization 'foo<std::__1::vector<int, std::__1::allocator<int> > &>' requested here
foo(v);
^
I don't have access to any other compiler that supports C++14 generic lambdas, so Clang is all I've tested.
Is this my bug or Clang's bug?
I'm leaning toward the idea that it's a bug in Clang's template instantiation rules: Clang seems to be treating auto as a first-class citizen in the type world, instead of as an indicator to perform type deduction.
test.cc:34:14: error: invalid argument type 'auto' to unary expression
bar((+forward_if<Container>)(elt));
^~~~~~~~~~~~~~~~~~~~~~
Again:
template<class T> T x{0}; // good
template<class T> auto x = T{0}; // bad
int main()
{
return x<int> + x<long>;
}
bad.cc:6:19: error: invalid operands to binary expression ('auto' and 'auto')
Per #dyp's and #ildjarn's comments: Yes, this was a bug in Clang 3.4 (and maybe 3.5). It is fixed in Clang 3.7.0 (and perhaps earlier). Various versions of Clang are available on Wandbox.
And here is the C++14 code I was trying to construct!
#include <type_traits>
template <class T>
struct forward_wrapper {
template <class U, class Enable = std::enable_if_t<(sizeof(std::remove_reference<U>), std::is_reference<T>::value)>>
static U forward(U&& u) {
return u;
}
template <class U, class Enable = std::enable_if_t<(sizeof(std::remove_reference<U>), !std::is_reference<T>::value)>>
static decltype(auto) forward(U&& u) {
return static_cast<typename std::remove_reference<U>::type &&>(u);
}
};
template <class T> auto forward =
[](auto&& u) -> decltype(auto) { return forward_wrapper<T>::forward(u); };
// --------
#include <stdio.h>
#include <vector>
void bar(int &) { puts("Called copy function"); }
void bar(int &&) { puts("Called move function"); }
template<class Container>
void foo(Container&& c) {
for (auto&& elt : c) {
bar(forward<Container>(elt));
}
}
int main() {
std::vector<int> v = {1,2};
foo(v);
foo(std::move(v));
}