Focus on the template arguments
I can create a stack (an adapter class template from standard library) object like this,
stack<int, vector<int>> myStack;
I know the second template argument means the underlying data structure of the stack. But why the following line doesn't give a compile time error?
stack<int, vector<string>> myStack;
Notice that I'm declaring a stack to contain elements of type int, but at the same time I'm declaring the underlying data structure to hold string elements.
Functionally, It works as if it was a stack of string elements.
What you are doing is undefined behaviour. Nevertheless, I am going to explain why it seems to work fine.
The container adapter std::stack<T, TContainer> contains several type symbols that are aliases for types that will be commonly used. There is a list here.
One that concerns us here is std::stack::value_type. It basically determines what type the methods std::stack::push and friends expect:
void push( const value_type& value );
We can also see how it is defined:
using value_type = typename TContainer::value_type
So, the type that is used in all actions is actually only based on the second type, TContainer ! In your case, that is vector<string>::value_type, so value_type will be an alias to string. The type used for T, int in your case, is not used.
Thus, everything seems to work.
But even though this works in your case with your particular compiler, it is actually not allowed:
The behavior is undefined if T is not the same type as Container::value_type. (since C++17)
You can find the source for this quote here.
From the documentation:
T - The type of the stored elements. The behavior is undefined if T is not the same type as Container::value_type. (since C++17)
Undefined behaviour means it might compile, it might even work, or it might wipe your hard drive.
How it fails, if it does, is implementation dependant.
If I had to guess, I would imagine that your implementation is discarding the int template argument, and just uses Container::value_type instead.
Once we get to C++17, this will be explicitly illegal. Some compilers will already not appreciate this code..
For example:
#include <stack>
#include <string>
#include <vector>
int main() {
std::stack<int, std::vector<std::string>> s;
s.push(3);
}
Fails to compile under OS X (clang, libc++, c++11) with:
blah.cc:7:7: error: no matching member function for call to 'push'
s.push(3);
~~^~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/stack:194:10: note:
candidate function not viable: no known conversion from 'int' to 'const value_type' (aka
'const std::__1::basic_string<char>') for 1st argument
void push(const value_type& __v) {c.push_back(__v);}
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/stack:197:10: note:
candidate function not viable: no known conversion from 'int' to 'value_type' (aka 'std::__1::basic_string<char>') for
1st argument
void push(value_type&& __v) {c.push_back(_VSTD::move(__v));}
^
1 error generated.
Related
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.
Consider the following Smallest Recreate-able Standard Compliant Code
#include <vector>
#include <memory>
struct Foo
{
int m_field1;
Foo(int field1):m_field1(field1){};
};
typedef unsigned long DWORD;
typedef unsigned short WORD;
struct BitField {
struct {
DWORD Field1:31;
DWORD Field2:1;
} DUMMY;
};
int main()
{
std::vector<std::shared_ptr<Foo>> bar;
BitField *p = new BitField();
//This Line compiles
auto sp1 = std::shared_ptr<Foo>(new Foo((DWORD)p->DUMMY.Field1));
//But std::make_shared fails to compile
auto sp2 = std::make_shared<Foo>((DWORD)p->DUMMY.Field1);
return 0;
}
This code fails to compile in VC11 Update 2 with the following error message
1>Source.cpp(23): error C2664: 'std::shared_ptr<_Ty> std::make_shared<Foo,DWORD&>(_V0_t)' : cannot convert parameter 1 from 'DWORD' to 'unsigned long &'
1> with
1> [
1> _Ty=Foo,
1> _V0_t=DWORD &
1> ]
I cross checked on IDEONE, and it compiled successfully. Am I missing something obvious?
A connect Bug was opened https://connect.microsoft.com/VisualStudio/feedback/details/804888/with-language-extension-enabled-vc11-an-explicit-cast-is-not-creating-an-rvalue-from-bit-fields
This is an odd one. The following snippet compiles under the /Za (disable language extensions) compiler flag, but not without:
struct {
unsigned field:1;
} dummy = {0};
template<class T>
void foo(T&&){}
int main(){
foo((unsigned)dummy.field);
}
Error without /Za:
error C2664: 'foo' : cannot convert parameter 1 from 'unsigned int' to 'unsigned int &'
This is obviously a bug, since the cast to unsigned should simply create an rvalue, which should not be deduced as an lvalue-reference and which should not be treated as a bit-field. I have a feeling the extension for "rvalues bind to lvalue-references" plays a role here.
Please file a bug report on Microsoft Connect.
Here's more of a comment than an answer. It may shed some light on what's happening.
Example by Xeo
struct {
unsigned field:1;
unsigned nonfield;
} dummy = {0};
template<class T>
void foo(T&&){}
Step one: Type deduction.
[class.bit]/1 specifies "The bit-field attribute is not part of the type of the class member." Consequently, type deduction for foo(dummy.field) deduces the template parameter to be unsigned&.
Step two: overload resolution.
Although not strictly necessary here, the Standard has a nice example concerning this in [over.ics.ref]/4
[Example: a function with an “lvalue reference to int” parameter can be a viable candidate even if the corresponding argument is an int bit-field. The formation of implicit conversion sequences treats the int bit-field as an int lvalue and finds an exact match with the parameter. If the function is selected by overload resolution, the call will nonetheless be ill-formed because of the prohibition on binding a non-const lvalue reference to a bit-field (8.5.3). —end example ]
So this function is well-formed and will be selected, but the call will be ill-formed nevertheless.
Step three: Workarounds.
The OP's conversion should resolve the problem, foo( (unsigned)dummy.field ), as it yields an rvalue which leads to T being deduced as unsigned and the parameter unsigned&& is initialized from a temporary. But it seems that MSVC ignores the lvalue-to-rvalue conversion if source and destination have the same type. Writing foo( (unsigned)dummy.nonfield ) deduced T as T& as well (even with a static_cast).
The lvalue-to-rvalue conversion required to deduce T to unsigned rather than unsigned& can be enforced by using a unary +: foo( +dummy.field )
The compiler's error message is correct insofar as it really can't create a DWORD& from the value you pass in. The bitfield isn't the right size to be a real reference to DWORD. Whether the compiler is correct to reject your program, I can't say.
It's easy to work around, though. Simply specify the second template parameter when you call make_shared:
auto sp2 = std::make_shared<Foo, int>(p->DUMMY.Field1);
I used int because that's the constructor's argument type. You could say DWORD instead; any non-reference numeric type would probably be sufficient. You can then also forego the type-casting to DWORD. It doesn't do anything more.
Bit fields can't have references but are sometimes treated sorta like lvalues because they can be assigned to. Bit fields are messy IMO and you should avoid them.
but if you need to convert a bitfield to behaving exactly like an rvalue of the same type you can use a function like below.
template<class T>
T frombits(const T& x) {
return x;
}
//...
std::make_shared<Foo>(frombits(p->DUMMY.Field1));
I'm rather against specifying the template type. When you can, and always when it is intended, let the compiler deduce the type. Template argument deduction can get messy in C++11 but it has been engineered to work very well and work as it should in almost every case. Don't help the compiler and don't think you know better than it; eventually you will loose.
I really don't understand this, I thought that compiler first executes what is in braces and then gives the result to the most appropriate function. Here it looks like it gives the function an initializer list to deal with it...
#include <string>
#include <vector>
using namespace std;
void func(vector<string> v) { }
void func(vector<wstring> v) { }
int main() {
func({"apple", "banana"});
}
error:
<stdin>: In function 'int main()':
<stdin>:11:27: error: call of overloaded 'func(<brace-enclosed initializer list>)' is ambiguous
<stdin>:11:27: note: candidates are:
<stdin>:6:6: note: void func(std::vector<std::basic_string<char> >)
<stdin>:8:6: note: void func(std::vector<std::basic_string<wchar_t> >)
Why isn't my func(vector<string> v) overload called, and can I make it so?
This one was subtle.
std::vector has a constructor taking two range iterators. It is a template constructor (defined in 23.6.6.2 of the C++11 Standard):
template<typename InputIterator>
vector(InputIterator first, InputIterator last,
const allocator_type& a = allocator_type());
Now the constuctor of std::vector<wstring> accepting an initializer_list is not a match for the implicit conversion in your function call, (const char* and string are different types); but the one above, which is of course included both in std::vector<string> and in std::vector<wstring>, is a potentially perfect match, because InputIterator can be deduced to be const char*. Unless some SFINAE technique is used to check whether the deduced template argument does indeed satisfy the InputIterator concept for the vector's underlying type, which is not our case, this constructor is viable.
But then again, both std::vector<string> and std::vector<wstring> have a viable constructor which realizes the conversion from the braced initializer list: hence, the ambiguity.
So the problem is in the fact that although "apple" and "banana" are not really iterators(*), they end up being seen as such. Adding one argument "joe" to the function call fixes the problem by disambiguating the call, because that forces the compiler to rule out the range-based constructors and choose the only viable conversion (initializer_list<wstring> is not viable because const char* cannot be converted to wstring).
*Actually, they are pointers to const char, so they could even be seen as constant iterators for characters, but definitely not for strings, as our template constructor is willing to think.
Consider the following code to iterate over an intrusive list using the BOOST_FOREACH macro:
#include <boost/foreach.hpp>
#include <boost/intrusive/list.hpp>
typedef boost::intrusive::list<
boost::intrusive::list_base_hook<> > MyList;
void iterate (const MyList& xs) {
BOOST_FOREACH (MyList::const_reference node, xs);
}
int main () {
MyList xs;
iterate (xs);
return 0;
}
Given boost version 1.48 the code fails with clang 3.2 (SVN) and gcc 4.6.3, but works with gcc 4.5.3. With non-const-qualified parameter xs to iterate the code works. With C++11 enabled all of the compilers accept the code. When using boost-1.46 both gcc versions accept the code, but clang still doesn't.
Is the code at hand a misuse of the BOOST_FOREACH macro, or is the error at boosts side? Is there a workaround that is nicer than iteration with regular for-loop?
Edit:
I pasted the error messages to pastebin (both are very verbose) for GCC and clang.
Here is what I could gather from the logs as well as my deductions as to the cause of failure.
Short version: for some reason BOOST_FOREACH attempts to copy the data which is not possible.
There is a note on the Extensibility page:
Making BOOST_FOREACH Work with Non-Copyable Sequence Types
For sequence types that are non-copyable, we will need to tell BOOST_FOREACH to not try to make copies. If our type inherits from boost::noncopyable, no further action is required. If not, we must specialize the boost::foreach::is_noncopyable<> template [...] Another way to achieve the same effect is to override the global boost_foreach_is_noncopyable() function. Doing it this way has the advantage of being portable to older compilers.
From the diagnosis, it is unclear whether the type is properly configured, so you might want to give it a go.
Pruned diagnosis and analysis.
/usr/include/boost/foreach.hpp:571:37: error: no matching constructor for initialization of 'boost::intrusive::list< >'
::new(this->data.address()) T(t);
^ ~
/usr/include/boost/foreach.hpp:648:51: note: in instantiation of member function 'boost::foreach_detail_::simple_variant<boost::intrusive::list< > >::simple_variant' requested here
return auto_any<simple_variant<T> >(*rvalue ? simple_variant<T>(t) : simple_variant<T>(&t));
^
/usr/include/boost/intrusive/list.hpp:1490:35: note: candidate constructor not viable: 1st argument ('const boost::intrusive::list< >') would lose const qualifier
BOOST_MOVABLE_BUT_NOT_COPYABLE(list)
^
/usr/include/boost/move/move.hpp:371:7: note: expanded from macro 'BOOST_MOVABLE_BUT_NOT_COPYABLE'
TYPE(TYPE &);\
/usr/include/boost/intrusive/list.hpp:1497:4: note: candidate constructor not viable: no known conversion from 'const boost::intrusive::list< >' to 'const value_traits' (aka 'const boost::intrusive::detail::base_hook_traits<boost::intrusive::list_base_hook< >, boost::intrusive::list_node_traits<void *>, 1, boost::intrusive::default_tag, 1>') for 1st argument;
list(const value_traits &v_traits = value_traits())
^
/usr/include/boost/intrusive/list.hpp:1506:4: note: candidate constructor not viable: no known conversion from 'const boost::intrusive::list< >' to '::boost::rv<list< >> &' for 1st argument;
list(BOOST_RV_REF(list) x)
^
/usr/include/boost/intrusive/list.hpp:1502:4: note: candidate constructor template not viable: requires at least 2 arguments, but 1 was provided
list(Iterator b, Iterator e, const value_traits &v_traits = value_traits())
^
I tried to isolate the error as much as possible (removing backtraces etc..) Apparently the problem stems from boost::intrusive::list, and more precisely the inability to build a new boost::intrusive::list<> from a boost::intrusive::list<> const.
The most promising constructor is defined by a macro:
BOOST_MOVABLE_BUT_NOT_COPYABLE(list)
which expands to
list(list&);
which is the way boost emulates move semantics for non-copyable types in C++03. However it cannot move from a const item since the const qualifier would be lost.
This looks to be part of the trickery used by BOOST_FOREACH to avoid multiple evaluation of the container argument (in case it is a function invocation) though I am a little surprised it tries to copy the argument here.
Since you are using gcc > 4.6 and clang 3.2, you could use C++11's range-based for looops:
#include <boost/foreach.hpp>
#include <boost/intrusive/list.hpp>
typedef boost::intrusive::list<
boost::intrusive::list_base_hook<> > MyList;
void iterate (const MyList& xs) {
for(const auto &node : xs) {
// do something with node
}
}
int main () {
MyList xs;
iterate (xs);
return 0;
}
You could also use std::for_each:
void iterate (const MyList& xs) {
std::for_each(xs.begin(), xs.end(),
[](MyList::const_reference node) {
// do something with node
}
);
}