C2998 cannot be a template definition error - c++11

I met a couple of C2998 error recently, here is the first one:
Case 1, I can do
template <typename T> struct X { static char value; };
template <> char X<int>::value = 'a';
char X<char>::value = 'b';
, that when defining a static member of a fully specified template class, I can use either template <> or omitted it, though I don't know why both work.
Case 2 also compile:
template <typename T> struct X2{};
template <> struct X2<int> { static char value; };
char X2<int>::value = 'a';
, that when defining a static member of a fully specified template class, and the static member is not in the unspecified template, with template <> omitted, it compiles.
Case 3, if I add template <> back, this will trigger a C2998 error on the 2nd line:
template <> struct X2<char> { static char value; };
template <> char X2<char>::value = 'b';
why is it?

Related

C++11 How to cast enum class template parameter to int?

I have a struct which takes enum class value on the template parameter.
template <typename EnumValue>
struct Type
{
public:
static constexpr int VALUE = static_cast<int>(EnumValue);
};
enum class MyEnum {Val1, Val2};
std::cout << Type<MyEnum::Val2>::VALUE << std::endl;
I expected it to work, but it gives errors:
error: expected primary-expression before ‘)’ token
static constexpr int VALUE = static_cast<int>(EnumValue);
error: type/value mismatch at argument 1 in template parameter list for ‘template<class EnumValue> struct Type’
std::cout << Type<MyEnum::Val2>::VALUE << std::endl;
How to fix these errors without changing the template parameters for Type?
Since I don't want the template inputs to be changed,
I don't want to use something like this:
template <typename EnumType, EnumType EnumValue>
struct Type...
You can't.
typename EnumValue declares a type, it is not a value. You can't have Type<MyEnum::Val2> with an enum class in C++11.
In C++17 you will be able to write template <auto EnumValue> which does what you want.
As #Caleth has mentioned, you need to specify the enum type and enum value to the Type struct. But as far as you do not want to change the template parameters, why not going for an static method inside Type?
template <typename EnumType>
struct Type
{
public:
static constexpr int getValue(EnumType value)
{
return static_cast<int>(value);
}
};
enum class MyEnum { Val1, Val2 };
int main()
{
std::cout << Type<MyEnum>::getValue(MyEnum::Val2) << std::endl;
}

CRTP traits only working with templated derived class

I have seen an idiom for using Derived type traits in the base class of a CRTP pattern that looks like this:
template<typename Derived>
struct traits;
template<typename Derived>
struct Base {
using size_type = typename traits<Derived>::size_type;
};
template <typename T>
struct Derived1 : Base<Derived1<T>>{
using size_type = size_t;
void print(){ std::cout << "Derived1" << std::endl; }
};
template <typename T>
struct traits<Derived1<T>> {
using size_type = size_t;
};
int main()
{
using T = float;
Derived1<T> d1;
d1.print();
}
My understanding is that the purpose of the idiom is to delay the instantiation of the Base class's size_type. What I am confused by is the fact that this pattern only seems to work if the derived class is itself templated. For instance, if we change the code to:
template<typename Derived>
struct traits;
template<typename Derived>
struct Base {
using size_type = typename traits<Derived>::size_type;
};
struct Derived1 : Base<Derived1>{
using size_type = size_t;
void print(){ std::cout << "Derived1" << std::endl; }
};
template <>
struct traits<Derived1> {
using size_type = size_t;
};
int main()
{
Derived1 d1;
d1.print();
}
then we get the error
prog.cc: In instantiation of 'struct Base<Derived1>':
prog.cc:21:19: required from here
prog.cc:18:58: error: invalid use of incomplete type 'struct traits<Derived1>'
using size_type = typename traits<Derived>::size_type;
^
prog.cc:14:8: note: declaration of 'struct traits<Derived1>'
struct traits;
^~~~~~
prog.cc: In function 'int main()':
prog.cc:33:9: error: 'Derived1' is not a template
Derived1<float> d1;
Could somebody give me an explanation indicating why the templated derived class compiles, but the untemplated class does not?
The issue you're seeing has nothing to do with CRTP.
Here's what the standard mentions.
If a class template has been declared, but not defined, at the point of instantiation (13.7.4.1),
the instantiation yields an incomplete class type (6.7). [Example:
template<class T> class X; X<char> ch; // error: incomplete type
X<char>
Your traits has only been declared at the point of instantiation of Base<Derived>, hence as per the standard(see above extraction from the standard), struct traits<Derived> yields an incomplete type.
You should reorder the code so that it sees the traits<Derived> specialization when Base<Derived> gets instantiated.
The compilation error you are seeing has nothing to do with CRTP, it's just a bit of a mish-mash of dependencies.
In the code without the templation, your "Base" struct needs the definition of the specialized "traits" struct but it only appears afterwards, so it tries to use the incomplete type it saw in the declaration above.
To get the code to work you need to have the "traits" specialization before the Base declaration, which requires you to also add a declaration of Derived 1, here is a compiling code:
class Derived1;
template<typename Derived>
struct traits;
template <>
struct traits<Derived1> {
using size_type = size_t;
};
template<typename Derived>
struct Base {
using size_type = typename traits<Derived>::size_type;
};
struct Derived1 : Base<Derived1>{
using size_type = size_t;
void print(){ std::cout << "Derived1" << std::endl; }
};
int main()
{
Derived1 d1;
d1.print();
}

How to properly create template object of type T i.e. T result = T();

Hi I am trying to create an object of type T where T is a pointer via the use of T result = T(). But instead of calling the constructor it simply returns a null pointer.
Here is an example of some affected code:
template <class T>
T readBlockchain(std::ifstream* stream) {
T result = T(); // Result is null after this
decltype(result->getLastBlock()) blkPtr = result->getLastBlock();
auto blk = *blkPtr;
decltype(result->getLastBlock()) lastBlock = &readBlock<decltype(blk)>(stream);
if(!lastBlock->verify())
return nullptr;
unsigned long count = *readUnsignedLong(stream);
unsigned long orphanCount = *readUnsignedLong(stream);
std::map<std::string, decltype(blk)> blocks = std::map<std::string, decltype(blk)>();
for(int i = 0; i < count - 1; i++){
decltype(blk) block = readBlock<decltype(blk)>(stream);
if(!block.verify())
return nullptr;
blocks.insert(std::make_pair(block.getHash(), block));
}
std::vector<Blockchain<decltype(blk)>*> orphanedChains = std::vector<Blockchain<decltype(blk)>*>();
for(int i = 0; i < orphanCount - 1; i++){
Blockchain<decltype(blk)>* orphan = &readOrphanedChain<Blockchain<decltype(blk)>>(stream);
orphanedChains.push_back(orphan);
}
result->setLastBlock(lastBlock);
result->setCount(count);
result->setOrphanCount(orphanCount);
result->setBlocks(blocks);
result->setOrphanedChains(orphanedChains);
return result;
}
If my understanding is correct. In order to generalize your readBlockchain correctly, you would want when T is a pointer to create a new object of T in the heap and when T is a concrete type to create a regular T object by calling the constructor of T. One solution would be to use the following specialization construct.
template<typename T>
struct CreateNew {
template<typename... Args>
static T apply(Args&&... args) { return T(std::forward<Args>(args)...); }
};
template<typename T>
struct CreateNew<T*> {
template<typename... Args>
static decltype(auto) apply(Args&&... args) { return std::make_unique<T>(std::forward<Args>(args)...); }
};
That is, you could create a template class that takes a template argument T along with a specialization of that template class for pointers of type T*. Inside the primary template (e.g., static member function apply) you'll create objects of type T by calling the constructor of class T and inside the specialization you'll create heap objects of T* (Notice that in the specialization I return a std::unique_ptr<T*> for convenience).
Thus, your readBlockChain template function would become:
template <class T>
decltype(auto) readBlockchain(std::ifstream* stream) {
auto result = CreateNew<T>::apply(/* T constructor arguments */);
...
return result;
}
Live Demo

the most clean way to static_assert 3 or more items in oneline

How to static_assert 3 items to be same at compile time like this.
union
{
std::uint32_t multibyte;
std::uint8_t bytes[4];
} test;
static_assert(sizeof(test) == sizeof(test.multibyte) == sizeof(test.bytes), "Union size mismatch.");
So of course the static_assert here fails because the last check will be 1 == 4. Is there more clean way besides
static_assert(sizeof(test.bytes) == sizeof(test.multibyte) && sizeof(test) == sizeof(test.bytes), "Union size mismatch.");
You could write a struct for that:
template<typename...> struct AllSame;
template<typename Arg1, typename Arg2, typename... Args>
struct AllSame<Arg1, Arg2, Args...>
{
static constexpr bool value = sizeof(Arg1) == sizeof(Arg2) && AllSame<Arg1, Args...>::value;
};
template<typename Arg>
struct AllSame<Arg>
{
static constexpr bool value = true;
};
Not tested, might contain errors.
If you are able to use c++14 then by the following example you can static_assert instances too, if they can be used in a constant expression:
template<typename T, typename... Ts>
constexpr bool compare_sizes(T&&, Ts&&...) noexcept {
using expand = int[];
bool result = sizeof...(Ts) > 0;
static_cast<void>(expand {
0, (static_cast<void>(result &= (sizeof(Ts) == sizeof(T))), 0)...
});
return result;
}
Example of use with your union { /* ... */ } test:
static_assert(compare_sizes(test, test.multibyte, test.bytes), "Size mismatch.");
Note that variable declaration and use of static_cast statement are prohibited in a constexpr function body until c++14.

Avoid narrowing type conversion on function call

Suppose you want to avoid narrowing when calling a function
void foo(char c) { /* ... */ }
foo(4); // get a compilation error here
You can define your target function, and a forwarding template overload that will attempt a non-narrowing conversion to the target type (DEMO):
void foo(char) {}
template <typename T>
void foo(T&& t) { foo(char{std::forward<T>(t)}); }
foo('a');
// foo(4); // ill-formed
foo({4});
// foo(0); // ill-formed
foo({0});
// foo(u'a'); // ill-formed
foo({u'a'});
int i = 2;
// foo(i); // ill-formed
// foo({i}); // ill-formed
This has the nice advantage that clients can force the conversion themselves by passing a braced-init-list. Since the braced-init-list impedes template deduction, only the target function can be selected by overload resolution. So even though e.g. foo(4) matches the template overload and is ill-formed - since int cannot in general be converted to char without narrowing - foo({4}) is well-formed since 4 can be converted to char without narrowing.
You can use a class template on T that has
1) a template constructor on a different type X that tries to instantiate the class when the parameter is not T
2) a constructor with T as param that takes care of the case where you are instantiating the class with the exact type
#include <iostream>
// g++-4.9 -Werror=narrowing -std=c++11 main2.cc
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55783
template <typename T>
struct no_narrowing
{
using type = T;
// this template constructor lets you try to
// instantiate a no_narrowing using a different
// type. if Narrowing would take place
// value{ val } takes care of it
template <typename X>
no_narrowing(X val) : value{val} {}
// if type is exactly T we use this
no_narrowing(type val) : value{val} {}
operator type() const { return value;}
type value;
};
template <typename T>
using nn = no_narrowing<T>;
void print(nn<char> v)
{
std::cout << v << std::endl;
}
int main(int argc, char *argv[])
{
int i = 2;
print('d');
print(i); // this will not compile
return 0;
}

Resources