This question is already asked most likely, but I did not find the answer.
The code below compiles with gcc but crashes at runtime, with std::length_error (live).
void test(const std::string &value) { std::cout << "string overload: " << value << std::endl; }
//void test(const std::vector<std::string> &) { std::cout << "vector overload" << std::endl; }
int main()
{
test({"one", "two"});
}
The ability to create a string from the initializer list of strings seems controversial and, for example, does not make it possible to create the overload commented out in the code above.
But even if such construction is allowed, why does it lead to a failure?
It calls
string(const char* b, const char* e)
string ctor overload.
It works only if b and e points to the same string literal. Otherwise it is undefined behaviour.
For starters there is no used the constructor that accepts an initializer list because such a constructor looks like
basic_string(initializer_list<charT>, const Allocator& = Allocator());
^^^^^
So the compiler searches another appropriate constructor and it finds such a constructor. It is the constructor
template<class InputIterator>
basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator());
That is the expressions "one" and "two" are considered as iterators of the type const char *.
So the function test has undefined behavior.
You could write for example (provided that string literals with the same content are stored as one string literal in memory, which is not guaranteed and depends on the selected compiler options).
#include <iostream>
#include <string>
void test(const std::string &value) { std::cout << "string overload: " << value << std::endl; }
//void test(const std::vector<std::string> &) { std::cout << "vector overload" << std::endl; }
int main()
{
test({ "one", "one" + 3 });
}
And you will get a valid result.
string overload: one
Pay attention to that this construction
{ "one", "two" }
is not an object of the type std::initializer_list<T>. This construction does not have a type. It is a braced-init-list that is used as an initialzer. Simply the compiler tries at first to use a constructor that have the first parameter of the type std::initializer_list to use with this initializer.
For example if you will use the class std::vector<const char *> then indeed the compiler will use its constructor with std::initializer_list and correspondingly initializes its parameter with this braced-init-list. For example
#include <iostream>
#include <vector>
int main()
{
std::vector<const char *> v( { "one", "two" } );
for ( const auto &s : v ) std::cout << s << ' ';
std::cout << '\n';
}
Related
This question already has answers here:
Why is there no implicit conversion from std::string_view to std::string?
(3 answers)
Closed 3 years ago.
What is the reason that the standard class std::basic_string does not have an "implicit" constructor that accepts as its argument an object of the type std::string_view?
I would expect that this program will compile.
#include <iostream>
#include <string>
#include <string_view>
struct A
{
void f( const char *s ) const
{
std::cout << "A::f( " << s << " )\n";
}
};
struct B
{
void f( const std::string &s ) const
{
std::cout << "B::f( " << s << " )\n";
}
};
template <typename... Bases>
struct C : Bases...
{
using Bases::f...;
};
int main()
{
C<A, B>().f( std::string_view( "Hello" ) );
// or
std::string_view s( "Hello" );
C<A,B>().f( s );
}
The reason is because many of the string_view accepting functions are ambiguous with the std::string overload of the same function. Take
std::string s({"abc", 1});
for example. This is either create a std::string from a std::string_view by calling the string_view accepting constructor or create a std::string via
basic_string(const CharT* s, size_type count, const Allocator& alloc = Allocator());
LWG 2758 brought this issue up originally and LWG 2946 finished adjusting all of the string_view taking functions to templates. By doing this none of the older string interface needed to be adjusted so older code wont break when compiled under C++17.
I'm having trouble with boost::variant (using boost 1.67.0).
When my template parameter list includes both bool and std::string, any variant objects which should be treated as string, seem to be implicitly bound to bool instead. For example:
using Varval = boost::variant<bool, std::string>;
void main()
{
std::vector<Varval> vect{ true, false, "Hello_World" };
std::cout << "[ ";
for (const auto &v : vect)
std::cout << v << " ";
std::cout << "]\n";
}
Outputs:
[ 1 0 1 ]
whereas if I change nothing but the first template argument, from bool to int, it works fine:
using Varval = boost::variant<int, std::string>;
void main()
{
std::vector<Varval> vect{ true, false, "Hello_World" };
std::cout << "[ ";
for (const auto &v : vect)
std::cout << v << " ";
std::cout << "]\n";
}
Properly outputs:
[ 1 0 Hello_World ]
Any ideas?
boost::variant has one constructor overload for each stated type. In your first example there will be one overload for bool and one for std::string. You are now calling the constructor with a char[n] which can be imlicitly converted to both of them. So there is no perfect match but two candidates. But instead of telling you that the call is ambiguous, the compiler will choose the bool overload as the better match.
Why? That is already perfectly answered in this question.
In your second example with int and std::string you are passing bools and char[n] to the constructors. bool is implicitly convertable to int but not to std::string. char[n] is implicitly convertable to std::string but not to int. So the according constructors are called as there is only one candidate for each.
I have a class, X, and it has the following constructors:
class X{
X(int64_t, int16_t, bool, int8_t);
X(int64_t, int16_t, bool);
X(double);
X(double, FT);
explicit X(const string&);
X(const string&, Y);
};
The problem is the compiler once created an X object passing just a bool (presume it was the double constructor which allowed this?) and it caused an issue.
To prevent this I made the bool constructor explicit and deleted it:
explicit X(bool) = delete;
but now I get compiler errors:
EXPECT_EQUALS(X("9.8901099"), a/b);
EXPECT_EQUALS(X{"56267"}, x);
X x{"-56267E-1"};
X b("5");
where the compiler says I have made use of deleted function explicit X(bool) = delete
How do I prevent an object of X being created from a bool?
This happens because bool is a better match for const char[] than std::string. When you have no ctor accepting bool then the std::string one is chosen. When you have a ctor with bool then this overload is selected. Only it is deleted so it is illegal to call it.
Lets see this behavior with a simple free overloaded function:
auto foo(int) { cout << "foo int" << endl; }
auto foo(std::string const &) { cout << "foo string" << endl; }
auto main() -> int {
foo(3); // int
foo("Asdf"); // std::string
}
When you add a bool overload:
auto foo(int) { cout << "foo int" << endl; }
auto foo(std::string const &) { cout << "foo string" << endl; }
auto foo(bool) { cout << "foo bool" << endl; }
auto main() -> int {
foo(3); // int
foo("Asdf"); // bool (also warning implicit conversion turn string literal into bool)
}
The solution is to add a char const * overload:
auto foo(int) { cout << "foo int" << endl; }
auto foo(std::string const &) { cout << "foo string" << endl; }
auto foo(char const *s) {
cout << "foo char const *" << endl;
// possibly:
return foo(std::string{s});
}
auto foo(bool) = delete;
auto main() -> int {
foo(3); // int
foo("Asdf"); // char const *
foo(true); // error
}
Because some legacy code uses and returns char * instead of char const * you might need to add a char * overload too.
First, you need to understand what happens if you explicitly delete the bool constructor. It becomes available in overload selection and when it is the best match it becomes used, giving a compiler error then because it is deleted.
This is different from when you do not define it, in that you can indeed 'stop' boolean values from converting via an implicit conversion plus followed by a (different) constructor of your class X, because it is the best match for a boolean, so implicit conversions stop there.
Consider the usage of your class
X x("abc");
Without the deleted bool constructor, this selects X(const string&).
With the deleted bool constructor, however, this will select the explicit X(bool), which is also deleted so this gives a compiler error. The solution to this, as Kerrek SB said, is to define a constructor for const char * as well.
And indeed, if you don't delete the bool constructor then the following selects the double
X x(true);
via first conversion to int and then conversion to double.
So, you can choose either solution, to delete or not to delete, but you need to be aware of the consequences and the requirements. If you delete it, you need to supply a const char * constructor. If you don't delete it, then you need to live with the fact that bool can be used to construct an instance of your class.
Right when I thought I understood what std::move and move constructors do, I tried to write some unit test, actually testing the move constructor for some class...
To my surprise I found, that I cannot think of a way to construct code which actually calls the move constructor. Worse, I cannot even set a breakpoint in the body of the move constructor (in VS2013 community edition, debug, 64bit build).
Wondering if this is a compiler peculiarity, I knocked up some small test code on my freebsd virtual machine, using clang (3.4.1). There, too I fail to find a way to get that move constructor invoked.
#include <iostream>
#include <stdint.h>
#include <string>
#include <algorithm>
#include <functional>
#include <ctype.h>
#include <locale>
void InPlaceToUpper( std::string& target )
{
std::transform(target.begin(), target.end(), target.begin(), ::toupper);
}
void InPlaceToLower( std::string& target )
{
std::transform(target.begin(), target.end(), target.begin(), ::tolower);
}
std::string ToUpper( const std::string& s )
{
std::string result;
result.resize(s.length());
std::transform(s.begin(), s.end(), result.begin(), ::toupper);
return result;
}
std::string ToLower( const std::string& s)
{
std::string result;
result.resize(s.length());
std::transform(s.begin(), s.end(), result.begin(), ::tolower);
return result;
}
class CFoo
{
std::string m_value;
public:
CFoo()
: m_value()
{
std::cout << "CFoo() called." << std::endl;
}
CFoo(const char *value)
: m_value(value)
{
std::cout << "CFoo(const char *) called." << std::endl;
}
CFoo(const std::string& value )
: m_value(value)
{
std::cout << "CFoo(const std::string&) called." << std::endl;
}
CFoo(const CFoo& other )
: m_value(other.m_value)
{
std::cout << "CFoo() copy constructor called." << std::endl;
}
CFoo(CFoo&& other )
: m_value(std::move(other.m_value))
{
std::cout << "CFoo() move constructor called." << std::endl;
std::cout << "other.m_value = " << other.m_value.c_str() << std::endl;
}
~CFoo()
{
std::cout << "~CFoo() called." << std::endl;
}
const CFoo& operator=( const CFoo& other )
{
std::cout << "CFoo copy assignment operator called." << std::endl;
if( &other != this )
{
m_value = other.m_value;
}
return *this;
}
const CFoo& operator=( CFoo&& other )
{
std::cout << "CFoo move assignment operator called." << std::endl;
if( &other != this )
{
m_value = std::move(other.m_value);
}
return *this;
}
CFoo ToUpper()
{
return CFoo(::ToUpper(m_value));
}
CFoo ToLower()
{
return CFoo(::ToLower(m_value));
}
const char * ToString() const
{
return m_value.c_str();
}
};
int main( int argc, const char *argv[] )
{
{
CFoo foo;
CFoo foo1("Hello World");
CFoo foo2 = CFoo("Hello again World!");
CFoo foo3(CFoo("Bye world"));
CFoo foo4 = CFoo("Bye again world");
CFoo foo5 = foo4.ToUpper();
CFoo foo6 = foo4.ToLower();
foo6 = foo4.ToUpper();
std::cout << "foo4: " << foo4.ToString() << std::endl;
foo6 = CFoo("Well well well");
}
return 0;
}
My apologies if the code is not as short as it might possibly be. But there are only a few spots to look at, namely my efforts to get the move constructor invoked in main() and the definition of the various constructors in class Foo.
I am aware of compiler settings which allow turning off RVO and stuff but for the purpose of using the feature "move constructor" in performance aware code, there should be a classic example of when it gets invoked. If that is not the case, I will probably decide not to even bother using move constructors at all.
To answer the question, you can tell me a line I can write in main() which gets the move constructor of CFoo called. Or you can tell me what I am doing wrong.
Does std::string support being moved like that? Maybe this is why my efforts fail?
Thanks, in advance.
In all your attempts to use the move constructor it is being elided, so that e.g. CFoo foo = CFoo(blah); is simply equivalent to CFoo foo(blah); which doesn't need to use the move constructor. This is a Good Thing because the compiler is optimising away the need for any copy or move to happen at all.
To see the move constructor being used try:
CFoo f1;
CFoo f2 = std::move(f1);
This constructs f2 from an rvalue, and nothing can be elided, so the move constructor will be used.
First of all, there is an error in std::string ToUpper(const std::string& s), namely there is no space in result. C++ algorithms do not grow their target containers automatically. You must either allocate space yourself, or use a inserter adaptor.
To make space in out, e.g. do this:
result.resize(s.length());
After it the move assignment operator called for:
foo6 = foo4.ToUpper();
foo6 = CFoo("Well well well");
The move constructor is called whenever an object is initialized from xvalue of the same type, which includes:
initialization, T a = std::move(b); or T a(std::move(b));, where b is of type T
function argument passing: f(std::move(a));, where a is of type T and f is void f(T t)
function return: return a; inside a function such as T f(), where a is of type T which has a move constructor.
For more see move constructors on cppreference.com.
What is most appropriate in const std::string assignment/declaration? Using the constructor(e.g., const std::string WORD("hello");) or using equal operator(e.g., const std::string WORD= "hello";)?
Does these things has difference in memory usage or time processes?
For any reasonable compiler, the code generated will be the same in both cases. Whether you should use direct initialization or copy-initialization in this case is essentially opinion-based.
In both cases, generally compilers will remove the copies using "Return Value Optimisation" anyway. See this code on ideone here calls neither the ordinary constructor nor the assignment operator as it doesn't print that they're being called to screen:
That is:
#include <iostream>
class C
{
public:
C() {}
C(const char[]) { std::cout << "Ordinary Constructor" << std::endl; }
C& operator=(const char[]) { std::cout << "Assignment Operator" << std::endl; return *this; }
};
int main() {
std::cout << "Start" << std::endl;
C x1 = "Hello";
C x2("Hello");
std::cout << "End" << std::endl;
}
Simply outputs:
Start
End
It doesn't output:
Start
Assignment Operator
Ordinary Constructor
End
As C++ allows the copies to be skipped and the temporary to be constructed in place.
The lines:
std::string x = "hello";
std::string x("hello");
both will only call the constructor of std::string. That is, they are identical, and neither will use the operator= overloads.