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.
Related
I'm interested in programmatically determining whether a delete-expression is valid for some given object. It seems like one should be able to do this using SFINAE. The code I have come up with works like I expect with Clang 6.0.1, but fails to compile with GCC 7.3.0.
Two questions: 1) is there a better way to achieve the goal, and 2) who's right?
#include <type_traits>
using namespace std;
// C++14 variant of void_t
template< typename ...T > struct void_type { typedef void type; };
template< typename ...T > using void_t = typename void_type<T...>::type;
template< typename T, typename = void_t<> > struct Deletable : false_type { };
template< typename T >
struct Deletable<T, void_t<decltype(delete declval<T>())> > : true_type { };
// Here's the SFINAE'd test ^^^^^^^^^^^^^^^^^^^
struct A {
operator signed *() const;
};
struct B {
operator signed *() const;
operator unsigned *() const;
};
int main() {
static_assert(Deletable<A>::value, "Couldn't delete A objects but "
"should be able to.");
static_assert(!Deletable<B>::value, "C objects are deletable but "
"shouldn't be because pointer-to-object is ambiguous.");
return 0;
}
My attempt at answering q.2
tl;dr: I think Clang is right. Skip to the bottom for a few other test cases.
Here's why, using N4140.
The relevant section about the delete-expression when given a class type reads [expr.delete] p.1:
[...] If of class type, the operand [of the delete-expression] is contextually implicitly converted to a pointer to object type. The delete-expression’s
result has type void.
Note that void * is not a "pointer to object". Here we find that the result of the decltype(delete <blah>) is void: it is not a dependent expression. However, I think for SFINAE we're just interested in whether or not it's well-formed.
The conversion is done according to [conv] p.5:
An expression e of class type E appearing [as a delete-expression's operand] is
said to be contextually implicitly converted to a specified type T and
is well-formed if and only if e can be implicitly converted to a type
T that is determined as follows: E is searched for conversion
functions whose return type is cv T or reference to cv T such that T
is allowed by the context. There shall be exactly one such T.
Since T needs to be a pointer-to-object, we see that class A meets the conversion criteria (and is therefore deletable) by providing a single conversion-to-object-pointer function whereas B, providing two such functions, does not. Thus, trying to delete a B object should result in an ill-formed expression. This is what the static_asserts in the above code are supposed to ensure.
Possible alternative approach: can one test an object for "convertability to some pointer to object type"?
Carrying on, here's the definition of an invalid expression for the purpose of type substitution [type.deduct] p.8:
If a substitution results in an invalid type or expression, type
deduction fails. An invalid type or expression is one that would be
ill-formed, with a diagnostic required, if written using the
substituted arguments. [...] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]
As far as I can tell, since deleting a B object is ill-formed (with diagnostic required), the decltype(delete <b>) expression should be invalid making SFINAE kick in. Deletable then ends up inheriting from false_type, and we're golden.
With Clang, that seems to be the case. With GCC, here's the result:
main.cpp: In substitution of ‘template<class T> struct Deletable<T, typename
void_type<T, decltype (delete (declval<T>)())>::type> [with T = B]’:
main.cpp:24:32: required from here
main.cpp:24:32: error: ambiguous default type conversion from ‘B’
static_assert(!Deletable<B>::value, "Shouldn't be able to delete B objects "
^~
main.cpp:24:32: note: candidate conversions include ‘B::operator int*()
const’ and ‘B::operator unsigned int*() const’
main.cpp:24:32: error: type ‘struct B’ argument given to ‘delete’, expected
pointer
My guess is that GCC does not consider the contextual implicit conversion as part of the immediate context, so the ambiguity becomes a hard error rather than just a type deduction failure. Given the apparent intent of the "immediate context" as defined in the note above, I think the conversion should be in the immediate context. Therefore, as above, I think Clang is right.
For completeness, other possible reasons an object may not be deletable are:
class C {
~C();
public:
operator C*() const;
};
struct D {
~D() = delete;
operator D*() const;
};
struct E {
operator E*() const;
private:
void operator delete(void*);
};
int main() {
static_assert(!Deletable<C>::value, "C objects are deletable but shouldn't "
"be because destructor is private.");
static_assert(!Deletable<D>::value, "D objects are deletable but shouldn't "
"be because destructor is deleted.");
static_assert(!Deletable<E>::value, "E objects are deletable but shouldn't "
"be because deallocation function is inaccessible.");
return 0;
}
Everything works fine in Clang, and, interestingly, the static_assert() for D passes for GCC, however the C and E cases fail.
main.cpp: In instantiation of ‘struct Deletable<C>’:
main.cpp:31:32: required from here
main.cpp:11:76: error: ‘C::~C()’ is private within this context
struct Deletable<T, void_t<T, decltype(delete declval<T>())> > : true_type { };
^
main.cpp:14:5: note: declared private here
~C();
^
main.cpp: In function ‘int main()’:
main.cpp:31:5: error: static assertion failed: C objects are deletable but shouldn't be because destructor is private.
static_assert(!Deletable<C>::value, "C objects are deletable but shouldn't "
^~~~~~~~~~~~~
main.cpp:31:20: error: ‘C::~C()’ is private within this context
static_assert(!Deletable<C>::value, "C objects are deletable but shouldn't "
^~~~~~~~~~~~
main.cpp:14:5: note: declared private here
~C();
^
main.cpp: In instantiation of ‘struct Deletable<E>’:
main.cpp:35:32: required from here
main.cpp:11:76: error: ‘static void E::operator delete(void*)’ is private within this context
struct Deletable<T, void_t<T, decltype(delete declval<T>())> > : true_type { };
^
main.cpp:27:10: note: declared private here
void operator delete(void*);
^~~~~~~~
main.cpp:35:5: error: static assertion failed: E objects are deletable but shouldn't be because deallocation function is inaccessible.
static_assert(!Deletable<E>::value, "E objects are deletable but shouldn't "
^~~~~~~~~~~~~
main.cpp:35:20: error: ‘static void E::operator delete(void*)’ is private within this context
static_assert(!Deletable<E>::value, "E objects are deletable but shouldn't "
^~~~~~~~~~~~
main.cpp:27:10: note: declared private here
void operator delete(void*);
^~~~~~~~
This question is spawned from
Passing a member function pointer to an overloaded class method into a template function.
You need not read that to understand this question. Probably both the questions will have the same answer.
I am getting compiler error for below simple code.
#include<set>
template<typename Return, typename T>
T ReceiveFuncPtr (Return (T::*Method)(const int&))
{
T obj; // Found and declared an object of actual container class
(obj.*Method)(1); // Some processing
return obj; // Returned that container class object with RVO
}
int main ()
{
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
}
The error is interesting:
In function 'int main()':
error: no matching function for call to 'ReceiveFuncPtr(<unresolved overloaded function type>)'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: candidate is:
note: template<class Return, class T> T ReceiveFuncPtr(Return (T::*)(const int&))
T ReceiveFuncPtr (Return (T::*Method)(const int&))
^
note: template argument deduction/substitution failed:
note: mismatched types 'const int&' and 'std::initializer_list<int>'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::value_type&& {aka int&&}'
note: couldn't deduce template parameter 'Return'
If you look at the notes closely then it appears that compiler is matching all the other methods except the right one! In this case compiler should have matched insert(const std::set<int>::value_type&) aka const int&. If I change the ReceiveFuncPtr() to match some other overload, it will again fail by skipping that overload.
To debug this situation, I created handcrafted version of std::set. But that compiles fine:
template<typename T, typename T2 = void>
struct MySet
{
std::pair<T,bool> insert (const T& i) { return std::pair<T,bool>(T(),true); }
std::pair<T,bool> insert (T&& i) { return std::pair<T,bool>(T(),true); }
void insert (std::initializer_list<T> i) { return false; }
}
int main ()
{
ReceiveFuncPtr(&MySet<int>::insert); // OK
}
After surfing, I came across this post:
What are the rules for function pointers and member function pointers to Standard functions?
Though it's related , it doesn't solve problem.
Question: Why member function substitution fails in case of standard library method when the the same thing passes for handwritten class method?
Update:
After looking at the correct answer, I am sure that insert cannot be used. The only way would be ugly typecasting which is an overkill for this problem.
One elegant solution is to use std::set<int>::emplace<const int&> which has only templated version unlike insert which has mix of template and non-template versions.
Call the function as below:
ReceiveFuncPtr(&std::set<int>::emplace<const int&>);
Above compiles fine.
The problem isn't with the insert functions you showed in MySet. The problem is with one of the ones you omitted. Specifically:
template< class InputIt >
void insert( InputIt first, InputIt last );
From [temp.deduct.call]:
When P is a function type, pointer to function type, or pointer to member function type:
— If the argument is an overload set containing one or more function templates, the parameter is treated
as a non-deduced context.
Since &std::set<int>::insert is precisely such an overload set, the parameter is a non-deduced context and cannot be resolved. Your example of MySet does not contain a function template overload for insert, which is why it works fine. If you add one, you'll see that it will also fail to compile.
This question is spawned from
Passing a member function pointer to an overloaded class method into a template function.
You need not read that to understand this question. Probably both the questions will have the same answer.
I am getting compiler error for below simple code.
#include<set>
template<typename Return, typename T>
T ReceiveFuncPtr (Return (T::*Method)(const int&))
{
T obj; // Found and declared an object of actual container class
(obj.*Method)(1); // Some processing
return obj; // Returned that container class object with RVO
}
int main ()
{
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
}
The error is interesting:
In function 'int main()':
error: no matching function for call to 'ReceiveFuncPtr(<unresolved overloaded function type>)'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: candidate is:
note: template<class Return, class T> T ReceiveFuncPtr(Return (T::*)(const int&))
T ReceiveFuncPtr (Return (T::*Method)(const int&))
^
note: template argument deduction/substitution failed:
note: mismatched types 'const int&' and 'std::initializer_list<int>'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::value_type&& {aka int&&}'
note: couldn't deduce template parameter 'Return'
If you look at the notes closely then it appears that compiler is matching all the other methods except the right one! In this case compiler should have matched insert(const std::set<int>::value_type&) aka const int&. If I change the ReceiveFuncPtr() to match some other overload, it will again fail by skipping that overload.
To debug this situation, I created handcrafted version of std::set. But that compiles fine:
template<typename T, typename T2 = void>
struct MySet
{
std::pair<T,bool> insert (const T& i) { return std::pair<T,bool>(T(),true); }
std::pair<T,bool> insert (T&& i) { return std::pair<T,bool>(T(),true); }
void insert (std::initializer_list<T> i) { return false; }
}
int main ()
{
ReceiveFuncPtr(&MySet<int>::insert); // OK
}
After surfing, I came across this post:
What are the rules for function pointers and member function pointers to Standard functions?
Though it's related , it doesn't solve problem.
Question: Why member function substitution fails in case of standard library method when the the same thing passes for handwritten class method?
Update:
After looking at the correct answer, I am sure that insert cannot be used. The only way would be ugly typecasting which is an overkill for this problem.
One elegant solution is to use std::set<int>::emplace<const int&> which has only templated version unlike insert which has mix of template and non-template versions.
Call the function as below:
ReceiveFuncPtr(&std::set<int>::emplace<const int&>);
Above compiles fine.
The problem isn't with the insert functions you showed in MySet. The problem is with one of the ones you omitted. Specifically:
template< class InputIt >
void insert( InputIt first, InputIt last );
From [temp.deduct.call]:
When P is a function type, pointer to function type, or pointer to member function type:
— If the argument is an overload set containing one or more function templates, the parameter is treated
as a non-deduced context.
Since &std::set<int>::insert is precisely such an overload set, the parameter is a non-deduced context and cannot be resolved. Your example of MySet does not contain a function template overload for insert, which is why it works fine. If you add one, you'll see that it will also fail to compile.
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
I thought I had this figured out, but I guess I was wrong. I was under the impression that the first element in '<...>' is the type that is to be stored in the queue, the second is the container type (the choices being 'vector' or 'dequeue') and the third is the class that overloads the '()' operator for comparison. Based on this, I think the following code should compile, but it does not :(
std::priority_queue<uint32_t*, std::vector<uint32_t*>, edgeComparator> q();
uint32_t* nodeEdge = new uint32_t[2];
nodeEdge[0] = startN;
nodeEdge[1] = 0;
q.push(nodeEdge);
'edgeComparator' is defined as follows:
class edgeComparator
{
public:
bool operator() (const uint32_t*& lhs, const uint32_t*& rhs) const
{
return (lhs[1]>rhs[1]);
}
};
Here is the error I'm getting:
./Graph.cpp: In member function `void Graph::findShortestPath()':
./Graph.cpp:148: error: request for member `push' in `q', which is of non-class type `std::priority_queue<uint32_t*, std::vector<uint32_t*, std::allocator<uint32_t*> >, edgeComparator> ()()'
Worse yet, I also get this error when trying 'q.empty()'
./Graph.cpp:150: error: request for member `empty' in `q', which is of non-class type `std::priority_queue<uint32_t*, std::vector<uint32_t*, std::allocator<uint32_t*> >, edgeComparator> ()()'
You had:
std::priority_queue<uint32_t*, std::vector<uint32_t*>, edgeComparator> q();
But instead, you should use:
std::priority_queue<uint32_t*, std::vector<uint32_t*>, edgeComparator> q;
The first version declares a function named q, that takes no arguments, and returns a value of type std::priority_queue<...>.
The second version declares a variable named q, of type std::priority_queue<...>, which is default-initialised.