I have a bunch of overloaded functions to take on specific int sizes, float, double, char and std::string.
eg:
#include <cstdint>
#include <iostream>
void some_func(uint8_t& src) {
std::cout << "inside uint8_t" << std::endl;
}
void some_func(uint16_t& src) {
std::cout << "inside uint16_t" << std::endl;
}
void some_func(uint32_t& src) {
std::cout << "inside uint32_t" << std::endl;
}
void some_func(uint64_t& src) {
std::cout << "inside uint64_t" << std::endl;
}
void some_func(bool& src) {
std::cout << "inside bool" << std::endl;
}
void some_func(double& src) {
std::cout << "inside double" << std::endl;
}
void some_func(float& src) {
std::cout << "inside float" << std::endl;
}
void some_func(const char& src) {
std::cout << "inside char" << std::endl;
}
int main() {
some_func((bool)true);
some_func((double)13.4);
return 0;
}
For some reason the overloaded const char& src function is called instead of double and bool. They both have explicit function overloads written, so why is the compiler promoting? moreover double is 64bits, so how is that being coverted to char? Can I solve this with template specialization? (I need to perform specifc actions depending on the data type)
Note: I am on Visual studio 2019 compiling with std=c++17
Non-const lvalue references can't be initialized with rvalues. (bool)true; and (double)3.14 are rvalues.
The only overload with a const lvalue reference parameter (which can be initialized with rvalues) is the const char& src one, that's why it's used.
Since the type is different (char vs bool/double), a temporary char is created from the argument, and then is bound to the reference.
Related
Consider the following code
#include <iostream>
#include <cstring>
template <int size>
void func(const char (&arr)[size])
{
//Prefer this function for const string literals.
std::cout << "Array of " << size - 1 << " bytes" << std::endl;
}
void func(const char *arr)
{
//Use this for all other strings that are not literals
std::cout << "String of " << strlen(arr) << " bytes" <<std::endl;
}
int main()
{
func("Hello!");
return 0;
}
How do I ensure that the template function is called in the example instead of the overload?
What I would like it that the templated function is called on string literals while the other is available for all other cases. With the templated function, i can avoid calling the strlen(). In my application both functions finally create a string_view object.
I would like to know how an overload is preferred.
The problem is that array-to-pointer conversions count as exact match rank when ranking standard conversion sequences during overload resolution. Since the array overload is a template the pointer overload thus becomes a better match.
You could add a proxy object for the pointer overload, to force it into a worse conversion sequence than that of the array overload for when the argument is of type const char (&)[N]:
namespace detail {
struct Proxy {
Proxy(const char *ptr) : ptr_{ptr} {};
operator const char *() const { return ptr_; }
private:
const char *ptr_;
};
} // namespace detail
template <int size> void func(const char (&arr)[size]) {
// Prefer this function for const string literals.
std::cout << "Array of " << size - 1 << " bytes" << std::endl;
}
void func(detail::Proxy const &proxy) {
// Use this for all other strings that are not literals
std::cout << "String of " << strlen(proxy) << " bytes" << std::endl;
}
int main() {
func("Hello!"); // Array of 6 bytes
const char *str = "Hello!";
func(str); // String of 6 bytes
}
The trick with a reference makes inexact match to const char* for a regular function.
#include <iostream>
#include <cstring>
template <size_t size>
void func(const char (&arr)[size])
{
//Prefer this function for const string literals.
std::cout << "Array of " << size - 1 << " bytes" << std::endl;
}
void func(const char *&arr)
{
//Use this for all other strings that are not literals
std::cout << "String of " << strlen(arr) << " bytes" <<std::endl;
}
int main()
{
func("Hello!");
const char *s = "s";
func(s);
return 0;
}
Output
Array of 6 bytes
String of 1 bytes
I have a simple program like this:
#include <iostream>
#include <string_view>
class C {
public:
void print(std::string_view v) { std::cout << "string_view: " << v << std::endl; }
void print(bool b) { std::cout << "bool: " << b << std::endl; }
};
int main(int argc, char* argv[]) {
C c;
c.print("foo");
}
When I run it, it prints bool: 1
How can I get C++ to prefer the string_view implicit conversion instead of the bool implicit conversion?
You can turn the string_view overload into a template function, and add a constraint to it so that it has a higher preference than the bool overload when it receives a type that can be converted to string_view.
#include <string_view>
class C {
public:
template<class T>
std::enable_if_t<std::is_convertible_v<const T&, std::string_view>>
print(const T& v) { std::cout << "string_view: " << std::string_view(v) << std::endl; }
void print(bool b) { std::cout << "bool: " << b << std::endl; }
};
Demo.
So our C++ library lets users pass in lists of values wrapped in classes with variadic constructors so the lengths of these lists can be checked at compile time. I'm trying to add some new functionality by adding new overloaded functions. However, the 'wrong' overload is being called when I pass a zero-length array of parameters e.g. {}. The following minimal example illustrates the issue:
#include <iostream>
#include <vector>
class EmptyList
{
public:
template<typename... T>
EmptyList(T&&... vals)
{
static_assert(sizeof...(vals) == 0, "Wrong number of values");
}
std::vector<double> getValues() const { return {}; }
};
void testVector(int *a, std::vector<double> b)
{
std::cout << "Vector: A (" << a << ")" << std::endl;
}
void testVector(std::vector<double> a, std::vector<double> b)
{
std::cout << "Vector: B" << std::endl;
}
void testInitList(int *a, std::initializer_list<double> b)
{
std::cout << "Init list: A (" << a << ")" << std::endl;
}
void testInitList(std::initializer_list<double> a, std::initializer_list<double> b)
{
std::cout << "Init list: B" << std::endl;
}
void testEmptyList(int *a, const EmptyList &b)
{
std::cout << "Empty list: A (" << a << ")" << std::endl;
}
void testEmptyList(const EmptyList &a, const EmptyList &b)
{
std::cout << "Empty list: B" << std::endl;
}
int main()
{
testVector({}, {});
testInitList({}, {});
testEmptyList({}, {});
}
The output is:
Vector: A (0)
Init list: B
Empty list: A (0)
Not only does the overloading behaviour seems weird, but there seems to be some sort of compiler special case for std::initializer_list making it behave differently to both my class and std::vector. Is there any way of working around this so the function overload taking my class is chosen over the one taking the pointer?
Is there any way of working around this so the function overload taking my class is chosen over the one taking the pointer?
Not without an explicit cast. When performing implicit conversion, the conversion to pointer will always be given higher preference than to a user defined type.
May be duplicated to this.
I read Effective Modern C++. Under Item 1, I found a case for universal reference:
For the last example f(27); I did a test under VS2013.
void process(int& x)
{
std::cout << "int&" << std::endl;
}
void process(int&& x)
{
std::cout << "int&&" << std::endl;
}
template<typename T>
void f(T&& param)
{
std::cout << "------------------------------------------------" << std::endl;
if (std::is_lvalue_reference<T>::value)
{
std::cout << "T is lvalue reference" << std::endl;
}
else if (std::is_rvalue_reference<T>::value)
{
std::cout << "T is rvalue reference" << std::endl;
}
else
{
std::cout << "T is NOT lvalue reference" << std::endl;
}
std::cout << "param is: " << typeid(param).name() << std::endl;
process(std::forward<T>(param));
process(param);
}
int getINT()
{
int x = 10;
return x;
}
int _tmain(int argc, _TCHAR* argv[])
{
f(10);
f(getINT());
return 0;
}
Here is the output:
------------------------------------------------
T is NOT lvalue reference
param is: int
int&&
int&
------------------------------------------------
T is NOT lvalue reference
param is: int
int&&
int&
I found that within the template function, without std::forward<T>(param), process(int& x) will be called but according to the book, the type for param should be rvalue reference, so process(int&& x) should be called. But this is not the case. Is it I misunderstand something?
Here is the forwarding reference I found from other thread:
You're confusing types with value categories. As a named parameter, param is an lvalue, then for process(param); process(int& x) will be called.
That's why we should use std::forward with forwarding reference; std::forward<T>(param) converts param to rvalue for this case, then process(int&& x) will be called (as expected).
I have found this answer to the question "Does a const reference prolong the life of a temporary?", which states:
Only local const references prolong the lifespan.
I'm afraid my standardese is not up to scratch to know whether foo, below, is a local const reference or not.
Does my const std::string& foo below prolong the lifetime of the temporary std::string function argument created in the call to get_or, or do I have a dangling reference?
#include <iostream>
#include <boost/optional.hpp>
struct Foo
{
const std::string& get_or(const std::string& def)
{
return str ? str.get() : def;
}
boost::optional<std::string> str;
};
int main()
{
Foo f;
const std::string& foo = f.get_or("hello world");
std::cout << foo << '\n';
}
const& won't extend lifetimes in that situation. Consider the example here that constructs a temporary and then attempts to print it: it's using the same constructs as your code, but I've altered it to make object construction and destruction more explicit to the user.
#include <iostream>
struct reporting {
reporting() { std::cout << "Constructed" << std::endl;}
~reporting() { std::cout << "Destructed" << std::endl;}
reporting(reporting const&) { std::cout << "Copy-Constructed" << std::endl;}
reporting(reporting &&) { std::cout << "Move-Constructed" << std::endl;}
reporting & operator=(reporting const&) { std::cout << "Copy-Assigned" << std::endl; return *this;}
reporting & operator=(reporting &&) { std::cout << "Move-Assigned" << std::endl; return *this;}
void print() const {std::cout << "Printing." << std::endl;}
};
const reporting& get_or(const reporting& def)
{
return def;
}
int main()
{
const reporting& foo = get_or(reporting{});
foo.print();
return 0;
}
Output:
Constructed
Destructed
printing.
Note how the object is destroyed before printing. is displayed.
You might be wondering why the code still completes with no visible errors: it's the result of Undefined Behavior. The object in question doesn't exist, but because it doesn't depend on state to invoke its method, the program happens to not crash. Other, more complicated examples should carry no guarantee that this will work without crashing or causing other, unexpected behavior.
Incidentally, things are a little different if the temporary is bound directly to the const&:
#include <iostream>
struct reporting {
reporting() { std::cout << "Constructed" << std::endl;}
~reporting() { std::cout << "Destructed" << std::endl;}
reporting(reporting const&) { std::cout << "Copy-Constructed" << std::endl;}
reporting(reporting &&) { std::cout << "Move-Constructed" << std::endl;}
reporting & operator=(reporting const&) { std::cout << "Copy-Assigned" << std::endl; return *this;}
reporting & operator=(reporting &&) { std::cout << "Move-Assigned" << std::endl; return *this;}
void print() const {std::cout << "printing." << std::endl;}
};
const reporting& get_or(const reporting& def)
{
return def;
}
int main()
{
const reporting& foo = reporting{};
foo.print();
return 0;
}
Output:
Constructed
printing.
Destructed
See how the object isn't destroyed until after it is used. In this situation, the object survives until the end of scope.
You passed the string through too many references.
Binding the temporary string to the def parameter of get_or extends the lifetime of the string to the end of the full expression containing the function call, but binding def to the return value of get_or and binding the return value of get_or to foo do not extend the lifetime further. The string is dead by the time you try to print it.
The "temporary" in question is the std::string-object created when calling get_or with a parameter of type const char*. The lifetime of this temporary object is limited with the end of function get_or, and the fact that you return a reference to this temporary and assign it afterwards does not prolong the lifetime. See the following code which uses a simple "custom" string class, which couts construction and destruction:
class MyString {
public:
MyString (const char* str) {
m_str = strdup(str);
cout << "constructor MyString - '" << m_str << "'" << endl;
}
~MyString() {
cout << "destructor MyString - '" << m_str << "'" << endl;
free(m_str);
}
char *m_str;
};
struct Foo
{
const MyString& get_or(const MyString& def)
{
cout << "Foo::get_or with '" << def.m_str << "'" << endl;
return def;
}
};
int main()
{
Foo f;
const MyString& foo = f.get_or("hello world");
cout << "usage of foo?" << endl;
}
Output:
constructor MyString - 'hello world'
Foo::get_or with 'hello world'
destructor MyString - 'hello world'
usage of foo?
Note that the destructor is called before you will have the chance to use foo.
The situation is different if you assign a reference to a temporary directly. Again, the lifetime is until the end of the function main, but it will be used in main and not in any function calling main:
const MyString& foo2 = MyString("hello world2");
cout << "usage of foo..." << endl;
Then the output will be:
constructor MyString - 'hello world2'
usage of foo...
destructor MyString - 'hello world2'