I want to have templated member variable, inited with constructor, like this:
struct S{
//typedef S ThisT;
template<class IdsT, class DataT>
struct CallbackPack{
/*constexpr */CallbackPack(IdsT &selfIds):
selfIds(selfIds){}
const IdsT &selfIds;
};
const CallbackPack<S, S> callbackPack123 = CallbackPack<S, S>((*this));
};
But when I try to compile this I get error.
g++-4.8 -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp:16:65: error: expected ';' at end of member declaration
const CallbackPack<S, S> callbackPack123 = CallbackPack<S, S>((*this));
^
main.cpp:16:66: error: expected unqualified-id before '>' token
const CallbackPack<S, S> callbackPack123 = CallbackPack<S, S>((*this));
^
main.cpp:16:65: warning: non-static const member 'const S::CallbackPack<S, S> S::S' in class without a constructor [-Wuninitialized]
const CallbackPack<S, S> callbackPack123 = CallbackPack<S, S>((*this));
^
main.cpp:16:62: error: wrong number of template arguments (1, should be 2)
const CallbackPack<S, S> callbackPack123 = CallbackPack<S, S>((*this));
^
main.cpp:9:12: error: provided for 'template<class IdsT, class DataT> struct S::CallbackPack'
struct CallbackPack{
^
http://coliru.stacked-crooked.com/a/ea9ed4482306ce17
As Mooing Duck said, this is compiler bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52595.
To avoid it, you should add parentheses:
const CallbackPack<S, S> callbackPack123 = (CallbackPack<S, S>(*this));
EDIT:
If I'm understanding what you want right, try out this code:
#include <iostream>
#include <tuple>
#include <functional>
struct S{
S() : callbackPack123(*this) {}
template<class IdsT, class DataT>
struct CallbackPack{
// Initializing reference with nullptr is NOT A GOOD THING.
// I'm just doing it to prove that no default constructors are called.
CallbackPack() : selfIds_(nullptr) {
std::cout << "EMPTY CONSTRUCTOR CALLED\n";
}
CallbackPack(IdsT &selfIds) : selfIds_(selfIds) {
std::cout << "IdsT CONSTRUCTOR CALLED\n";
}
const IdsT &selfIds_;
};
const CallbackPack<S, S> callbackPack123;
};
int main()
{
S s;
return 0;
}
It prints
IdsT CONSTRUCTOR CALLED
and nothing else, indicating that the member has been created with the IdsT constructor. I hope this answers your question.
Related
As practice I tried to make a wrapper around raw pointers to have cleaner memory management when using CUDA. Basically I made a PointerBase template that has a PointerBase specialization for const pointers. This template is the base template for a HostPointer and a DevicePointer template that manage memory on CPU side and GPU side respectively (but this is out of scope of this question). I struggle with the conversion from a PointerBase<T> object into the PointerBase<const T> specialization.
(Keep in mind this is more about practicing C++ templates than production use).
Here is a simple code demonstrating the behavior.
template <typename T>
struct Pointer
{
T* ptr_;
Pointer() = default;
Pointer<T>& operator=(const Pointer<T>& other) = default;
operator Pointer<const T>() const { return Pointer<const T>(ptr_); }
T* get() const { return ptr_; }
};
template <typename T>
struct Pointer<const T>
{
const T* ptr_;
Pointer() = default;
Pointer<const T>& operator=(const Pointer<const T>& other) = default;
const T* get() const { return ptr_; }
};
template <typename T>
T dereference(Pointer<const T> data)
{
return *data.get();
}
int main()
{
int value = 7.0;
Pointer<int> ptr{&value};
//int tmp3 = dereference(ptr); // not compiling
int tmp4 = dereference(Pointer<const int>(ptr)); // compiling
return 0;
}
This code fails to compile with the following error (gcc 8.4.0 on ubuntu 20.4, with c++14 standard)
main.cpp: In function ‘int main()’:
main.cpp:41:31: error: no matching function for call to ‘dereference(Pointer<int>&)’
int tmp3 = dereference(ptr); // not compiling
^
main.cpp:32:3: note: candidate: ‘template<class T> T dereference(Pointer<const T>)’
T dereference(Pointer<const T> data)
^~~~~~~~~~~
main.cpp:32:3: note: template argument deduction/substitution failed:
main.cpp:41:31: note: types ‘const T’ and ‘int’ have incompatible cv-qualifiers
int tmp3 = dereference(ptr); // not compiling
^
Why is the conversion operator not considered for calling the dereference function ?
I feel there is a connection to the fact that the Pointer<const T> type is a specialization of the Pointer template. But I still don't understand why a conversion is not happening.
I get the same result when defining a constructor in Pointer<const T> taking a Pointer<T> as a parameter.
Any help ?
PS : Interestingly, I get the same behavior with std::shared_ptr.
#include <memory>
template <typename T>
T dereference(std::shared_ptr<const T> data)
{
return *data.get();
}
int main()
{
std::shared_ptr<int> sptr(new int[10]);
int tmp1 = dereference(sptr); // not compiling
//int tmp2 = dereference(std::shared_ptr<const int>(sptr)); //compiling
return 0;
}
main.cpp: In function ‘int main()’:
main.cpp:9:32: error: no matching function for call to ‘dereference(std::shared_ptr<int>&)’
int tmp1 = dereference(sptr); // not compiling
^
main.cpp:4:3: note: candidate: ‘template<class T> T dereference(std::shared_ptr<const _Tp>)’
T dereference(std::shared_ptr<const T> data) { return *data.get(); }
^~~~~~~~~~~
main.cpp:4:3: note: template argument deduction/substitution failed:
main.cpp:9:32: note: types ‘const _Tp’ and ‘int’ have incompatible cv-qualifiers
int tmp1 = dereference(sptr); // not compiling
^
Trying to propose a solution to another question, I bumped (again) my nose against code that compile and work with clang++ (3.5) but give compilation error with g++ (4.9.2)
The following is a simplified minimal (I hope) example
#include <iostream>
class foo;
template <std::size_t>
struct bar;
template <>
struct bar<0U>
{ static void baz (foo const & f); };
class foo
{
int i = 42;
template <std::size_t I>
friend void bar<I>::baz (foo const &);
};
void bar<0U>::baz (foo const & f)
{ std::cout << f.i << std::endl; }
int main()
{
foo f;
bar<0U>::baz(f);
}
The error from my g++ is
test_114-98,11,14,clang.cpp:18:41: error: member ‘void bar<<anonymous> >::baz(const foo&)’ declared as friend before type ‘bar<<anonymous> >’ defined
friend void bar<I>::baz (foo const &);
^
test_114-98,11,14,clang.cpp: In static member function ‘static void bar<0ul>::baz(const foo&)’:
test_114-98,11,14,clang.cpp:15:13: error: ‘int foo::i’ is private
int i = 42;
^
test_114-98,11,14,clang.cpp:22:19: error: within this context
{ std::cout << f.i << std::endl; }
^
My question, as usual, is: who's right and who's wrong.
The following code compiles fine with g++ and fails with clang (all versions I've tested):
#include <iostream>
namespace has_insertion_operator_impl
{
typedef char no;
typedef char yes[2];
struct any_t
{
template <typename T>
any_t(const T&);
};
yes& testStreamable(std::ostream&);
no testStreamable(no);
no operator<<(const std::ostream&, const any_t&);
template <typename T>
struct has_insertion_operator
{
static std::ostream& s;
static const T& t;
static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes);
};
} // namespace has_insertion_operator_impl
template <typename T>
struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T>
{};
enum A : bool {
Yup = true,
Nop = false,
};
template <typename T>
bool getTraitVal(const T&) { return has_insertion_operator<T>::value; }
int main() { std::cout << getTraitVal(A::Yup) << std::endl; }
The error (with clang only!) is this:
prog.cc:24:59: error: use of overloaded operator '<<' is ambiguous (with operand types 'std::ostream' (aka 'basic_ostream<char>') and 'const A')
static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes);
I believe this is a small enough example. Here are links to online compilers for it:
clang 3.8
g++ 6.1
When I change the enum type from bool to int - the error disappears.
So why is this happening? This was originally discovered when using the doctest and Catch testing frameworks - here is the bug report for Catch. Could it be a clang bug?
I know it doesn't answer your question, but it seems clang has a problem with enums of underlying type 'bool'.
I further reduced your example to:
#include <iostream>
enum A : bool {
Yup = true,
Nop = false,
};
int main() {
A t = Yup;
std::cout << t;
}
And here you can already have a feeling for what's happening:
prog.cc:10:15: error: use of overloaded operator '<<' is ambiguous (with operand types 'ostream' (aka 'basic_ostream<char>') and 'A')
std::cout << t;
~~~~~~~~~ ^ ~
/usr/local/libcxx-3.8/include/c++/v1/ostream:195:20: note: candidate function
basic_ostream& operator<<(bool __n);
^
/usr/local/libcxx-3.8/include/c++/v1/ostream:198:20: note: candidate function
basic_ostream& operator<<(int __n);
^
...
Consider (link):
#include <cstdlib>
#include <cassert>
#pragma pack (1)
template <size_t Width>
class Base
{
public:
char mData [Width];
template <typename Field> Field ExtractAs () const
{
return *reinterpret_cast <Field> (mData);
}
};
template <typename FieldVal>
class IntegralField
:
public Base <sizeof (FieldVal)>
{
public:
FieldVal GetVal () const
{
return ExtractAs <FieldVal> ();
}
};
int main()
{
char raw[4] = {0x11, 0x22, 0x33, 0x44};
typedef IntegralField <uint32_t> UInt32Field;
const UInt32Field& field =
*reinterpret_cast <const UInt32Field*> (raw);
const uint32_t extracted = field.GetVal();
assert (extracted == 0x44332211);
}
The call:
return ExtractAs <FieldVal> ();
Fails to compile under g++ 4.7.2 with:
main.cpp: In member function ‘FieldVal IntegralField<FieldVal>::GetVal() const’:
main.cpp:25:16: error: ‘ExtractAs’ was not declared in this scope
main.cpp:25:35: error: expected primary-expression before ‘>’ token
main.cpp:25:38: error: expected primary-expression before ‘)’ token
main.cpp: In function ‘int main()’:
main.cpp:32:28: error: ‘uint32_t’ was not declared in this scope
main.cpp:32:36: error: template argument 1 is invalid
main.cpp:32:49: error: invalid type in declaration before ‘;’ token
main.cpp:35:11: error: ‘uint32_t’ does not name a type
main.cpp:36:5: error: ‘extracted’ was not declared in this scope
ninja: build stopped: subcommand failed.
I have tried a number of tricks including using Base::ExtractAs, typedefs, etc, to no avail.
Is what I'm trying to do possible in C++03? How can I call a function template in a base class template from a derived class template member function? Note that I cannot use C++11.
Edit: When I redefine GetVal to be more explicit about types:
FieldVal GetVal () const
{
static const size_t fieldSize = sizeof (FieldVal);
typedef Base <fieldSize> MyBase;
typedef FieldVal MyField;
return MyBase::ExtractAs <MyField> ();
}
I still get:
error: expected primary-expression before ‘>’ token
On: return MyBase::ExtractAs <MyField> ();
Edit: Here is the final, working code:
#include <cstdlib>
#include <cassert>
#include <stdint.h>
#pragma pack (1)
template <size_t Width>
class Base
{
public:
char mData [Width];
template <typename Field> Field ExtractAs () const
{
return *reinterpret_cast <const Field*> (mData);
}
};
template <typename FieldVal>
class IntegralField
:
public Base <sizeof (FieldVal)>
{
public:
FieldVal GetVal () const
{
return this->template ExtractAs<FieldVal>();
}
};
int main()
{
char raw[4] = {0x11, 0x22, 0x33, 0x44};
typedef IntegralField <uint32_t> UInt32Field;
const UInt32Field& field =
*reinterpret_cast <const UInt32Field*> (raw);
const uint32_t extracted = field.GetVal();
assert (extracted == 0x44332211);
}
You can either say:
return this->template ExtractAs<FieldVal>();
Or
return Base<sizeof(FieldVal)>::template ExtractAs<FieldVal>();
Since you are in a class template and the base is a template specialization, too, the names of base members are not automatically injected into the derived template. (Consider what happens if you specialize Base!)
By qualifying the name or using this->, you make the entire name dependent, and so it doesn't cause in error in the first phase. Also, since the name ExtractAs is dependent (being the nested name of a template), you have to disambiguate it as template.
Consider the following code:
#include <iostream>
#include <type_traits>
template<typename Type>
class Test
{
public:
constexpr Test(const Type val) : _value(val) {}
constexpr Type get() const {return _value;}
static void test()
{
static constexpr Test<int> x(42);
std::integral_constant<int, x.get()> i;
std::cout<<i<<std::endl;
}
protected:
Type _value;
};
int main(int argc, char *argv[])
{
Test<double>::test();
return 0;
}
Under g++ 4.7.1, it returns the error:
main.cpp: In static member function ‘static void Test<Type>::test()’:
main.cpp:13:48: error: invalid use of ‘Test<Type>::get<int>’ to form a pointer-to-member-function
main.cpp:13:48: note: a qualified-id is required
main.cpp:13:48: error: could not convert template argument ‘x.Test<Type>::get<int>()’ to ‘int’
main.cpp:13:51: error: invalid type in declaration before ‘;’ token
I do not understand the problem: is it a compiler bug or is it a real problem ?
How to solve it ?
It looks like a GCC bug, clang 3.2 compiles without any error