template class declaration - is not a constant expression - c++

I have the following class which I need to have two constants upon declaration.
template <int PAGE_DIV_SIZE, int BUFFERS_NUM>
class BufferPool {
//...
}
And
here is a test code for its use
void testBufferPool(const int pageDivSize, const int bufferNum){
// other code and declaration
BufferPool <pageDivSize, bufferNum> bufferPool(catalog, devNum, hostCapacityVec, devCapacityVec);
}
I get the following error:
error: ‘pageDivSize’ is not a constant expression
BufferPoolTest.cpp:26:39: note: in template argument for type ‘int’
BufferPoolTest.cpp:26:39: error: ‘bufferNum’ is not a constant expression
BufferPoolTest.cpp:26:39: note: in template argument for type ‘int’
BufferPoolTest.cpp:26:51: error: invalid type in declaration before ‘(’ token
BufferPoolTest.cpp:26:100: error: expression list treated as compound expression in initializer [-fpermissive]
BufferPoolTest.cpp:26:100: error: cannot convert ‘std::vector<long unsigned int>’ to ‘int’ in initialization

In order to instantiate template, compiler must know all template arguments at compile time. There is no way to figure out the values of pageDivSize and bufferNum at compile time. So template argument should not be a constant variable, but a constant expression.
http://en.cppreference.com/w/cpp/language/constant_expression

Related

too many arguments to function std::make_shared<vector>

I am missing something with std::make_shared. Can't it resolve the type of a std::initializer_list, or am I doing something else wrong?
#include <vector>
#include <memory>
class A {};
int main()
{
A a;
std::vector<A> veca{A(), A{}, a}; // this works ofc
std::vector<A> vecb({A(), A{}, a}); // this too
std::make_shared<std::vector<A>>(vecb); // and this, ofc
std::make_shared<std::vector<A>>({a}); // what's wrong here?
return 0;
}
Error:
main.cpp:21:41: error: too many arguments to function ‘std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = std::vector; _Args = {}]’
std::make_shared<std::vector<A>>({a});
^
In file included from /usr/include/c++/6/memory:82:0,
from main.cpp:10:
/usr/include/c++/6/bits/shared_ptr.h:632:5: note: declared here
make_shared(_Args&&... __args)
^~~~~~~~~~~
Live example: https://onlinegdb.com/r1DlHquDL
Consider the following minimal example of your problem:
template <typename... Ts>
void f(Ts&&...); // replacement for std::make_shared
int main()
{
f({1});
}
This case is described in the C++ Standard in [temp.deduct.call/1]:
Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives
std::initializer_list<P′> or P′[N] for some P′ and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list independently, taking P′ as separate function template parameter types P′i and the ith initializer element as the corresponding argument. In the P′[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context ([temp.deduct.type]).
In your case, the last sentence applies. Interestingly, the error message says something else with GCC, which is weird. With Clang, the error message is clear:
error: no matching function for call to 'f'
note: candidate template ignored: substitution failure: deduced incomplete pack <(no value)> for template parameter 'Ts'

Why doesn't this enum convert to int?

Why does the following code not compile under g++ (C++14), MSVC (C++14), or ARM (C++03)?
The named Error instance calls the integer constructor, but the anonymous Error instance does not resolve.
class Error
{
public:
Error(int err) : code_(err) {}
const int code_;
};
enum Value
{
value_1
};
int main()
{
// compiles
Error e(value_1);
// does not compile under G++, ARM, or MSVC
Error(value_1);
}
Example error under G++: (Coliru link)
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'int main()':
main.cpp:19:18: error: no matching function for call to 'Error::Error()'
Error(value_1);
^
main.cpp:4:5: note: candidate: Error::Error(int)
Error(int err) : code_(err) {}
^~~~~
main.cpp:4:5: note: candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: constexpr Error::Error(const Error&)
class Error
^~~~~
main.cpp:1:7: note: candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: constexpr Error::Error(Error&&)
main.cpp:1:7: note: candidate expects 1 argument, 0 provided
This comes from the same place as "The Most Vexing Parse" - the rule that if it can be a declaration, it is a declaration.
And surprisingly, you're allowed to put parentheses around the identifier in a variable declaration.
(I have no idea why, but I'm guessing that it simplified C's parser back in the day.)
The following are all valid declarations of int variables:
int (foo);
int (bar) = 0;
int (baz)(3);
int (twaddle)(baz);
The problem is that code
Error(value_1);
is a declaration of a variable value_1 of type Error.
This is a legacy from C language that uses expressions as part of type declaration.
For example int *i is a pointer to int because it says that expression *i should evaluate to type int. More examples of this:
int (*func)() is a pointer to function returning int because expression (*func)() evaluates to type int.
int *p[8] is an array of pointers to int because expression *p[x] evaluates to type int.
int (*p)[8] is a pointer to array of 8 int's (int[8]) because expression (*p)[x] evaluates to type int.
int (*(*p[8])())() is an array of 8 pointers to functions returning pointers to a function returning int because expression (*(*p[x])())() evaluates to type int.
Similarly int (i) is a plain variable of type int as expression (i) evaluates to type int.
And so because C++ inherits this from C, it uses parenthesis as part of type declaration, but also adds more syntax on top, leading to some unexpected results.
The rule applied by C++ here says to treat everything that can be a declaration as a declaration.
Similar confusion if often caused by code like this:
Error ec();
which is a forward declaration of a function ec that returns Error.
main.cpp:19:18: error: no matching function for call to 'Error::Error()'
Error(value_1);
The compiler tries to call the non-existent default constructor Error::Error() because it sees
Error(value_1);
as a variable declaration
Error value_1;
A declaration is allowed to have redundant parenthesis.

Guard against type violations in non type template parameters

I usually use std::size_t where integral constants are needed in template parameters. What I notice though, is that the type system doesn't protect me from users that are happy to pass negative numbers as arguments to those parameters.
For example the following compiles giving disastrous results :
#include <iostream>
template<std::size_t I>
struct E1
{
static void apply()
{
std::cout << I << std::endl;
}
};
template<typename T>
constexpr T a = T { -1 };
template<std::size_t... Is>
void E2()
{
for (auto&& i : {Is...}) std::cout << i << " ";
std::cout << std::endl;
}
int main()
{
E1<-1>::apply();
E2<-1, -2, -3>();
//std::cout << a<std::size_t>;
}
Demo
Interestingly this is not allowed for variable templates (uncommenting the last line in main causes a compilation error).
Is there any solution/workaround for the struct and function case ?
This is actually a GCC bug, as there should be a diagnostic(warning or error) for narrowing here. The bug report is Narrowing conversions not caught in non-type template parameters and it has the following example:
template< char> void f () {}
template<unsigned int> void g () {}
template<unsigned int> struct A {};
int main () {
f<1024> (); // ill-formed, `char { 1024 }` is a narrowing conversion,
// see [dcl.init.list]p7
g<-123> (); // ill-formed, `unsigned int { -123 }` is a narrowing
// conversion, see [dcl.init.list]p7
A<-123> a; // ill-formed, see previous comment
}
and it points out the relevant quotes from the C++ standard:
[temp.arg.nontype]p1;
A template-argument for a non-type, non-template template
parameeter shall be one of:
for a non-type template-aprameter of integral or enumeration
type, a converted constant expression (5.19) of the type of
the template-parameter
<snip />
[expr.const]p3;
A converted constant expression of type T is an expression,
implicitly converted to a prvalue of type T, where the converted
expression is a core constant expression and the implicit
conversion sequence contains only user-defined conversions,
lvalue-to-rvalue conversions (4.1), integral promotions (4.5), and
integral conversions (4.7) other than narrowing conversions
(8.5.4).
[ Note: gcc accepts testcase.cpp, while clang (correctly) issues
the relevant diagnostics. ]
as sehe noted clang correctly produces a diagnostic for this with the following error:
error: non-type template argument evaluates to -1, which cannot be narrowed to type 'std::size_t' (aka 'unsigned long') [-Wc++11-narrowing]
E1<-1>::apply();
^
error: no matching function for call to 'E2'
E2<-1, -2, -3>();
^~~~~~~~~~~~~~
As a work-around you can use -Wsign-conversion -Werror(both documented here) to warn about this case and turn it into an error (see it live). I originally thought this would be caught by -Wconversion but -Wsign-conversion is turned off by default in C++.
My clang refuses to compile this:
test.cpp|24 col 8| error: non-type template argument evaluates to -1, which cannot be narrowed to type 'std::size_t' (aka 'unsigned long') [-Wc++11-narrowing]
E1<-1>::apply();
^
test.cpp|25 col 5| error: no matching function for call to 'E2'
E2<-1, -2, -3>();
^~~~~~~~~~~~~~
test.cpp|16 col 6| note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Is'
void E2()
So, in c++11 it should already be disallowed.
Other than that, it's a non-issue, because the template argument will actually not be negative. Maybe add a static assertion that the value is within a specific range.

Return reference to template vector

I start from simple class declaration where I defined inline template method which returning reference to specyfic type of container.
class JPetParamManager
{
public:
enum ContainerType {kScintillator, kPM, kKB, kTRB, kTOMB};
std::vector<JPetScin> fScintillators;
std::vector<JPetPM> fPMs;
std::vector<JPetKB> fKBs;
std::vector<JPetTRB> fTRBs;
std::vector<JPetTOMB> fTOMBs;
template <typename T>
const std::vector<T>& getContainer(const JPetParamManager::ContainerType &p_containerType) const
{
switch(p_containerType)
{
case kScintillator:
return fScintillators;
case kPM:
return fPMs;
case kKB:
return fKBs;
case kTRB:
return fTRBs;
case kTOMB:
return fTOMBs;
}
}
}
In some another class method I want to return some container from class above:
void JPetAnalysisModuleKB::CreateOutputObjects(const char* outputFilename)
{
std::vector<JPetKB> l_KBs22 = m_manager.getParamManagerInstance().getContainer<JPetKB>(JPetParamManager::ContainerType::kKB);
}
When I want to run this method in main I have error like:
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h: In member function ‘const std::vector<_RealType>& JPetParamManager::getContainer(const JPetParamManager::ContainerType&) const [with T = JPetKB]’:
JPetAnalysisModuleKB.cpp:55:126: instantiated from here
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h:81:14: error: invalid initialization of reference of type ‘const std::vector<JPetKB>&’ from expression of type ‘const std::vector<JPetScin>’
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h:83:14: error: invalid initialization of reference of type ‘const std::vector<JPetKB>&’ from expression of type ‘const std::vector<JPetPM>’
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h:87:14: error: invalid initialization of reference of type ‘const std::vector<JPetKB>&’ from expression of type ‘const std::vector<JPetTRB>’
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h:89:14: error: invalid initialization of reference of type ‘const std::vector<JPetKB>&’ from expression of type ‘const std::vector<JPetTOMB>’
make: *** [JPetAnalysisModuleKB.o] Błąd 1
Introduction
Even though only one of your switch-labels will match and execute, the statement(s) associated with the others must still be valid.
The compiler is trying to tell you that not all of your returns can be used when returning a std::vector<T> const& (where T is the type passed to your function).
Explanation
The below instantiate getContainer in a way that makes it return a std::vector<PetKB>, but when instantiating the function the compiler will see that the case-label matched by kScintillator has a return of type std::vector<JPetScin>.
m_manager.getParamManagerInstance().getContainer<JPetKB> (JPetParamManager::kScintillator)
Since std::vector<JPetScin> cannot be converted to a std::vector<PetKB> the compiler complains and basically says that your code is ill-formed.
The same applies even if the switch-condition doesn't select a case where the return-type is different; all paths must be able to return, otherwise an application is ill-formed.

Compiling Error

When I am trying to compile this code
using namespace std;
namespace asf{
inline int operator|(int);
}
asf::operator|(int x){
return (x>1)?x*operator|(x-1):1;
}
int main(){
cout<<5|;
}
I am getting the following errors
[Error] 'int asf::operator|(int)' must have an argument of class or enumerated type
[Error] ISO C++ forbids declaration of 'operator|' with no type [-fpermissive]
[Error] 'int asf::operator|(int)' should have been declared inside 'asf'
[Error] 'int asf::operator|(int)' must have an argument of class or enumerated type
In function 'int main()':
[Error] expected primary-expression before ';' token
What is wrong? Please help.
As the error says, overloaded operators must have at least one argument of class or enumerated type. That's how the language works.
In addition, you cannot change the arity of an operator when overloading. You're tryning to define a unary |, which is also illegal. | must always take two arguments. The declaration of operator | can include one argument only if it's declared inside a class, in which case the left-hand operand is implicitly of the class's type.