c++ newbie question - How can the C++ compiler know that the parameter to a template function has has STL methods as members? in C# you tell a generic method that a parameter has a type constraint, most commonly. it must implement an interface, but with c++ templates there is no restriction on the parameter type.
#include <list>
#include <iostream>
using namespace std;
template <typename T>
void DisplayContents (const T& Input)
{
for (auto iElement = Input.cbegin() // no intellisense
; iElement != Input.cend()
; ++ iElement )
cout << *iElement << ' ';
cout << endl;
}
int main ()
{
std::list <int> listIntegers;
listIntegers.push_front (10);
listIntegers.push_front (2011);
listIntegers.push_back (-1);
listIntegers.push_back (9999);
DisplayContents(listIntegers);
// DisplayContents(99); // neither of these will compile
// DisplayContents(new string("")); //
return 0;
}
so, in the templated method DisplayContents<>(const T& Input) , there is no intellisense on Input. When you type the period character, no suggestions pop up (which isn't that suprising since the function parameter hasn't specified that the input must be a list or any other type of STL container).
However, if you try and send something that isn't an STL container into DisplayContents<>(const T& Input), then the compiler throws these errors:-
error C2100: illegal indirection
error C2228: left of '.cbegin' must have class/struct/union
error C3536: 'iElement': cannot be used before it is initialized
suggesting that the compiler does know something about the type of the parameter needing to have some basic characteristics.
Can anyone please explain how the compiler "knows" that cbegin() and * operator can be used when a list is sent as the parameter, but not when a string or an int is sent, when apparently the type isn't known as intellisense isn't picking up the method cbegin() ?
It's quite simple, really. The compiler will pretend that T is the type of argument you passed in and then proceed with compilation. If it runs into any errors then it will report those. As long as the type of argument you use would work if you hard-coded that type then it will work.
In your case, it fails with int because an int has no cbegin() method.
It fails with new std::string("") because the argument type becomes std::string * const &, and you can't call .cbegin() on this pointer. (You would have to call ->cbegin() instead.)
However, you can call it with std::string("") (note the lack of new) which will cause the argument to be const std::string &, and this will compile.
So it has nothing at all to do with the compiler "knowing that T represents a standard container." If you create a simple type with cbegin() and cend() methods and make sure that the return values from those methods can be incremented, dereferenced, and compared for equality, that type would work just as well.
(Here is a demo of your template function working with a user-defined type.)
Template used to make generic code.
And here the compiler will generate three overload functions for you.
void DisplayContents (const std::list<int>& Input)
void DisplayContents (const int& Input)
void DisplayContents (string* const & Input)
obviously version 2 and 3 won't compile, type const int& and string* const& does not have method cbegin() nor cend()
PS: version 3 param should be string* const& Input, thanks to cdhowie
Related
I have a class that has both implicit conversion operator() to intrinsic types and the ability to access by a string index operator[] that is used for a settings store. It compiles and works very well in unit tests on gcc 6.3 & MSVC however the class causes some ambiguity warnings on intellisense and clang which is not acceptable for use.
Super slimmed down version:
https://onlinegdb.com/rJ-q7svG8
#include <memory>
#include <unordered_map>
#include <string>
struct Setting
{
int data; // this in reality is a Variant of intrinsic types + std::string
std::unordered_map<std::string, std::shared_ptr<Setting>> children;
template<typename T>
operator T()
{
return data;
}
template<typename T>
Setting & operator=(T val)
{
data = val;
return *this;
}
Setting & operator[](const std::string key)
{
if(children.count(key))
return *(children[key]);
else
{
children[key] = std::shared_ptr<Setting>(new Setting());
return *(children[key]);
}
}
};
Usage:
Setting data;
data["TestNode"] = 4;
data["TestNode"]["SubValue"] = 55;
int x = data["TestNode"];
int y = data["TestNode"]["SubValue"];
std::cout << x <<std::endl;
std::cout << y;
output:
4
55
Error message is as follows:
more than one operator "[]" matches these operands:
built-in operator "integer[pointer-to-object]" function
"Setting::operator[](std::string key)"
operand types are: Setting [ const char [15] ]
I understand why the error/warning exists as it's from the ability to reverse the indexer on an array with the array itself (which by itself is extremely bizarre syntax but makes logical sense with pointer arithmetic).
char* a = "asdf";
char b = a[5];
char c = 5[a];
b == c
I am not sure how to avoid the error message it's presenting while keeping with what I want to accomplish. (implicit assignment & index by string)
Is that possible?
Note: I cannot use C++ features above 11.
The issue is the user-defined implicit conversion function template.
template<typename T>
operator T()
{
return data;
}
When the compiler considers the expression data["TestNode"], some implicit conversions need to take place. The compiler has two options:
Convert the const char [9] to a const std::string and call Setting &Setting::operator[](const std::string)
Convert the Setting to an int and call const char *operator[](int, const char *)
Both options involve an implicit conversion so the compiler can't decide which one is better. The compiler says that the call is ambiguous.
There a few ways to get around this.
Option 1
Eliminate the implicit conversion from const char [9] to std::string. You can do this by making Setting::operator[] a template that accepts a reference to an array of characters (a reference to a string literal).
template <size_t Size>
Setting &operator[](const char (&key)[Size]);
Option 2
Eliminate the implicit conversion from Setting to int. You can do this by marking the user-defined conversion as explicit.
template <typename T>
explicit operator T() const;
This will require you to update the calling code to use direct initialization instead of copy initialization.
int x{data["TestNode"]};
Option 3
Eliminate the implicit conversion from Setting to int. Another way to do this is by removing the user-defined conversion entirely and using a function.
template <typename T>
T get() const;
Obviously, this will also require you to update the calling code.
int x = data["TestNode"].get<int>();
Some other notes
Some things I noticed about the code is that you didn't mark the user-defined conversion as const. If a member function does not modify the object, you should mark it as const to be able to use that function on a constant object. So put const after the parameter list:
template<typename T>
operator T() const {
return data;
}
Another thing I noticed was this:
std::shared_ptr<Setting>(new Setting())
Here you're mentioning Setting twice and doing two memory allocations when you could be doing one. It is preferable for code cleanliness and performance to do this instead:
std::make_shared<Setting>()
One more thing, I don't know enough about your design to make this decision myself but do you really need to use std::shared_ptr? I don't remember the last time I used std::shared_ptr as std::unique_ptr is much more efficient and seems to be enough in most situations. And really, do you need a pointer at all? Is there any reason for using std::shared_ptr<Setting> or std::unique_ptr<Setting> over Setting? Just something to think about.
This question already has answers here:
How do I specify a pointer to an overloaded function?
(6 answers)
Closed 4 years ago.
I have a class which offers custom static comparators which can be used by std::sort. The following would compile just fine (stripped down to a minimal code example):
#include <vector>
#include <string>
#include <algorithm>
class StringUtils
{
public:
static bool customStringCompare(const std::string&, const std::string&) { return true; }
};
void test()
{
std::vector<std::string> testList;
std::sort(testList.begin(), testList.end(), StringUtils::customStringCompare);
}
Now, when I add an overload to the StringUtils class like
static bool customStringCompare(const char*, const char*) { return true; }
the following would work:
void test2()
{
std::string s1, s2;
StringUtils::customStringCompare(s1, s2);
}
However, the std::sort call above produces compiler error C2672 (No matching overload found), C2780 (expected 2 arguments - 3 supported), C2783 (template argument for "_Pr" could not be deduced) in MSVC 2015 Update 2.
Why does std::sort fail to find the matching overload in this case?
In your code std::sort takes a function pointer. How then a compiler can decide which function you want? IntelliSense shows the following error:
cannot determine which instance of overloaded function StringUtils::customStringCompare is intended
To use overloading, you can turn a comparator into a function object:
struct Comparator {
bool operator()(const std::string&, const std::string&) const {
return true;
}
bool operator()(const char*, const char*) const {
return true;
}
};
void test() {
std::vector<std::string> testList;
std::sort(testList.begin(), testList.end(), Comparator{});
}
Alternatively, since C++14 you can use a generic lambda function:
void test() {
std::vector<std::string> testList;
std::sort(testList.begin(), testList.end(),
[](const auto& s1, const auto& s2) {
return StringUtils::customStringCompare(s1, s2);
});
}
The issue is that there are two overloads and passing one to std::sort does not clarify which overload should be used1. The compiler cannot deduce this from the usage in the std::sort call. This makes sense: The type of the comparator argument of std::sort is simply a template argument, i.e. it’s completely unspecified: any overload works just as well as any other.
There are multiple workarounds and in practice I generally recommend passing a functor as shown in Evg’s answer.
But it’s important to understand that the error is simply caused by a type that cannot be deduced automatically. So, to make the code compile, it’s sufficient to specify the type explicitly; this will select a single overload:
std::sort(
testList.begin(),
testList.end(),
static_cast<bool (*)(std::string const&, std::string const&)>(StringUtils::customStringCompare)
);
Here we use static_cast to explicitly signal the type of the function (pointer) for the purpose of overload resolution.
1 And, frankly, every single mainstream compiler produces a rotten error message. This has been known for a long time and is entirely fixable. clang++ is slightly better than GCC and MSVC but honestly not by much. But even C#, a completely unrelated language, gives highly idiosyncratic errors in similar situations.
The following code doesn't work, it gives the following errors:
No matching function for call to object of type 'const comparer'
and
Call to object of type 'value_compare' (aka 'std::_1::_map_value_compare, int, comparer, true>') is ambiguous
Here is the code:
struct comparer
{
bool operator()(const std::string x, const std::string y)
{
return x.compare(y)<0;
}
};
int main(int argc, char **argv)
{
vector< map<string,int,comparer> > valMapVect;
map<string,int,comparer> valMap;
valMapVect.push_back(valMap);
}
It is compiled with Xcode 5.x (so on a Mac).
Somebody has an idea of what is wrong? I think it was working a while ago when I was compiling it on Linux. Is it possible?
It seems libc++ wants the function call operator in comparer to be a const member function:
struct comparer
{
bool operator()(const std::string x, const std::string y) const
{ // ^^^^^ fixes the problem
return x.compare(y)<0;
}
};
Personally I would pass the arguments as std::string const& (note the &) but that doesn't change whether libc++ likes the comparison object. I'm not, yet, sure if the standard mandates that the const is present. I didn't spot such a requirement which would imply that the comparison function would have to be kept as a mutable member. However, given that it is often stateless, it is desirable to derive from it to not waste any memory (i.e., take advantage of the empty base optimization) which is probably what libc++ does. It isn't quite clear whether
it is a bug in libc++, i.e., that the comparison function object has to be stored a mutable member.
in the standard to not mandate the function call operator to be const.
in the code using it to make the function call operator const.
The easiest fix is, however, to make function call operator const.
I'm an experienced developer in a few compiled OOP languages, particularly Object Pascal and C#. Have 'messed around' with C++ for years but recently started getting more serious about C++ development.
Most of concepts in C++ are quite easy for me to grasp based on my experience with other languages. But one thing that I'm finding quite difficult is the ways in which the const directive is used and how it behaves in C++.
In particular, at the moment I'm faced with this problem, using the TinyXML Library in Netbeans C++ IDE, on an Ubuntu 12.04 machine and default mingW/G++ tool chain:
I'm calling this function:
TiXmlNode::TiXmlNode* FirstChild()
In the TinyXML source there are two overloaded public versions of this function in class TiXmlNode:
const TiXmlNode* FirstChild() const { return firstChild; }
TiXmlNode* FirstChild() { return firstChild; }
They are identical except for the const directive. I assumed that the version called would be dependent on how I declared the variable I was loading from the function, for example:
const TiXmlNode* aNode = node->FirstChild();
Would call the const version of the function
TiXmlNode* aNode = node->FirstChild();
Would call the second version, without const.
But when I try to use the second form in my code, I get a compiler error:
error: invalid conversion from ‘const TiXmlNode*’ to ‘TiXmlNode*’
[-fpermissive]
Why is this happening? How do I use the version of the function without const? What am I missing here?
More - where can I find a good summary of the usages of const directive in C++ 11.
Here, the overload chosen depends on the type of node, not the type of aNode.
const at the end of the member function declaration indicates that the function may be called on a const instance of the object; a member function lacking const there cannot be. When both const and non-const overloads are present, overload resolution prefers the const version when applicable.
Apparently, node is of type const TiXmlNode*, so FirstChild() const overload is chosen, and the return value is of type const TiXmlNode*. This cannot be converted to TiXmlNode*.
Here's a textbook example. std::vector<T> provides two overloads of operator[], looking like this (greatly simplified):
template <typename T>
class vector {
T* storage_;
public:
T& operator[](size_t index) { return storage_[index]; }
const T& operator[](size_t index) const { return storage_[index]; }
};
With this, you could write:
vector<int> v;
v[0] = 42; // OK: calls int& operator[](size_t)
const vector<int> cv;
cv[0] = 42; // Error: calls const int& operator[](size_t) const
As expected, const vector can't be modified, while non-const one can be.
So I have a function called find, which has two versions:
template <typename T>
typename btree<T>::iterator btree<T>::find(const T& elem)
{
//Implementation
}
and the other is the const_iterator version:
template <typename T>
typename btree<T>::const_iterator btree<T>::find(const T& elem) const
{
//Implementation
}
In my test file when I do
btree<char>::iterator it = myTree.find('M');
Everything works fine, however when I use the const_iterator version:
btree<char>::const_iterator it = myTree.find('M');
It gives me the error
error: conversion from 'btree_iterator' to non-scalar type 'const_btree_iterator' requested
Which obviously means that find is only ever using the iterator (non const) version. I know that C++ is supposed to call the const_iterator version automatically - if I had done everything right. So the question is, what might I be doing wrong?
The iterator classes are:
class btree_iterator and class const_btree_iterator which is just a copy paste of btree_iterator with the names changed
Here are the full source code:
btree_iterator.h (includes const_btree_iterator) http://pastebin.com/zQnj9DxA
btree.h http://pastebin.com/9U5AXmtV
btree.tem http://pastebin.com/U9uV3uXj
All the standard containers implement conversion of non-const to const iterators (as specified in the requirements for the Container concept):
The type of iterator used to iterate through a container's elements. The iterator's value type is expected to be the container's value type. A conversion from the iterator type to the const iterator type must exist.
You need conversion constructor like so:
class btree_iterator;
class const_btree_iterator
{
// ....
public:
const_btree_iterator(const btree_iterator& rhs) { /* .... */ }
//optionally: const_btree_iterator& operator=(const btree_iterator& rhs) { /* .... */ }
};
I threw in the assignment operator too but I suppose it is redundant
The important bit here is that overload resolution is performed based only on the arguments to the function and not the result. In your particular case you have two different overloads and the difference is that the implicit this is constant in one of them, that overload will be picked up whenever static type of the object or reference on which the method is called is constant.
If you want to force dispatch to the constant overload, you can obtain a const reference and then call on that reference:
btree<char> const & r = myTree;
btree<char>::const_iterator it = r.find('M');
You should avoid this construct in real code, even if you use it for testing purposes. The reason is that the const and non-const overloads should have the same semantics and thus the behavior should be the same.
Also note that in standard containers, there is an implicit conversion from iterator to const iterator to support using const_iterators directly on non-const containers. You should do the same, that is, if you provide an implicit conversion from iterator to const_iterator, then you can just write:
btree<char>::const_iterator it = myTree.find('M');
... and it will work (will not test the find method, but will allow you to verify the const_iterator behavior)