Why does the following code compile, even though I am passing a std::string object to a explicit constructor which expects a nlohmann::json (to the library) object? My understanding is that std::string won't be implicit converted due to the explicit keyword.
Is it possible to change my code so that it will compile successful only when a nlohmann::json is passed?
I use Visual Studio 2019 in Debug Mode with /Wall.
#include <nlohmann/json.hpp>
struct A {
explicit A(nlohmann::json json) {
}
};
int main() {
std::string a = "hello";
A b(a);
}
Why does the following code compile, even though I am passing a std::string object to a explicit constructor which expects a nlohmann::json (to the library) object? My understanding is that std::string won't be implicit converted due to the explicit keyword.
The explicit in the A constructor just means that the A constructor must be called explicitly (which you are). The compiler is allowed to use implicit conversions when passing arguments to the A constructor, unless they use types that are also explicit themselves (which the nlohmann::json constructor is not).
Is it possible to change my code so that it will compile successful only when a nlohmann::json is passed?
You can pass the argument by a non-const reference, preventing the compiler from passing an implicitly created temporary object:
struct A {
explicit A(nlohmann::json &json) {
}
};
This might be overkill but the following appears to work while also allowing a const-reference as constructor argument:
#include <nlohmann/json.hpp>
#include <type_traits>
struct A {
template<typename T, typename = std::enable_if_t<std::is_same_v<T, nlohmann::json>>>
explicit A(const T& json) {
}
};
int main() {
const std::string a = "hello";
// A b(a); // ERROR
const auto json_val = nlohmann::json::parse("{}") ;
A c {json_val} ;
}
Related
I have a Value class which I use for wrapping and converting basic types.
class Value {
public:
template <typename T>
Value(const T &value) : value_(StringMaker<T>(value)) {} // Not marked explicit
// Example -> Convert to String
std::string AsString() const;
// Example -> Convert to Int
int AsInt(bool *err_flag = nullptr);
private:
const std::string value_;
// Helper method to convert values to a string
template <typename T>
std::string StringMaker(const T &value);
};
I have other classes which use the Value class e.g. the class Foo:
struct Foo {
explicit Foo(const Value &value) : bar(value) {}
const Value bar;
};
With usage as follows:
// Normal use
std::cout << "Value converted to \"std::string\": " << Value(100).AsString()
<< std::endl;
// Normal use via Foo class
std::cout << "Value converted to \"std::string\" via \"Foo\": "
<< Foo(100).bar.AsString() << std::endl;
and output:
Value converted to "std::string": 100
Value converted to "std::string" via "Foo": 100
I would like to make use of a implicit constructor for Value, but with cpplint and Cppcheck I get the warning:
Single-parameter constructors should be marked explicit. [runtime/explicit] [5]
If I add explicit to the Value constructor I get additional errors like:
error: no matching function for call to ‘Foo::Foo(int)’
<< Foo(100).bar.AsString() << std::endl;
Thus, is there a safe way to keep the static analysis tools happy and also use implicit conversion?
Here is a working example with additional use-cases.
Thus, is there a safe way to keep the static analysis tools happy [...]
Yes, make the converting constructor explicit ;)
You can only disable or ignore the warning or make the constructor explicit. Though the warning exists for a reason, implicit conversions can be a source of confusion. Because Values constructor is a template, you can pass any parameter to Foos constructor too. Latest when you want to add a second constructor for Foo this can be confusing, for example:
struct Foo {
explicit Foo(const Value &value) : bar(value) { /* do something */}
explicit Foo(int x): bar(x) { /* do something else */}
const Value bar;
};
Then Foo(100) suddenly calls a different constructor, which instead of something does something else. Hence I suggest to make contructor of Value explicit.
Thus, I cannot do explicit with only Foo(100)
explicit means just that: You have to explicitly call the constructor. Foo(100) does not explicitly call Values constructor, but Foo(Value(100)) does. The sequence of conversions is exactly the same, but the code is more clear.
So instead of:
std::cout << Foo(100).bar.AsString() << std::endl;
write
std::cout << Foo(Value(100)).bar.AsString() << std::endl;
Alternatively, find a way to silence the warning. In rare cases implicit conversions are just right, but you need to be aware of the implications.
I cannot understand why this program fails to compile both with g++ 7.3 or clang++ 5.0 using -std=c++14.
A can be initialized from a const int as shown. A const reference to A can also be create from a const int but call to f(const A &) with a const int fails. Why?
#include <iostream>
struct V {
int i;
template <class T>
V(const T &t) : i{t} {}
};
struct A {
int i;
A(V v) : i{v.i} {}
};
void f(const A &) {}
int main() {
const auto i = 42;
const A a1{i}; // OK
std::cout << a1.i << '\n'; // 42
const A &a2 = A{i}; // OK
std::cout << a2.i << '\n'; // 42
f(i); // no matching function for call to 'f'
return 0;
}
Given f(i);, copy initialization is applied. And i (with type const int) needs to be converted to A, two user-defined conversions are required; from const int to V, and from V to A. But only one user-defined conversion is allowed in one implicit conversion sequence.
Bot const A a1{i}; and const A &a2 = A{i}; are direct initialization, only one implicit conversion from i (with type const int) to the argument of A's constructor (i.e. V) is required, so they work fine.
Note the difference between copy initialization and direct initialization,
In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.
As a workaround, you can perform explicit conversion on i before passing it to f().
Converting i to an A for the purpose of the function call will require two user defined conversions (int -> V -> A). The standard places a hard limit of a single user defined conversion on each implicit conversion sequence.
The same would happen if you were to try and bind a2 to i "directly". So you need to do a functional style cast (A{i}) when giving an argument to f as well.
You need two consecutive implicit type conversion here however C++ can do single conversion implicitley for you. If you want to let the compiler to generate the correct typed code for you, use template for the function f like following.
template <typename T>
void f(const T & x) { std::cout << x << std::endl;}
The reason why you need two type conversion is because of having only one constructor which takes type V in the struct. If you want to get rid of two type conversion as a second solution, you can add another constructor which takes int as a parameter like following
struct A {
int i;
A(V v) : i{v.i} {}
A(int theI) : i{theI} { }
};
Two user defined conversions are not supported in copy initialization. Simple work around to the problem is to wrap i with A while passing to funtion f with f(A(i))
The following code compiles with clang 3.0/libc++:
#include <memory>
class Foo
{
public:
Foo()
: mem_(new int(10))
{
}
std::unique_ptr<int> mem_;
};
int main()
{
auto foo = std::make_shared<Foo>();
return 0;
}
But this one doesn't (std::string parameter added):
#include <memory>
#include <string>
class Foo
{
public:
Foo(const std::string& s)
: mem_(new int(10))
{
}
std::unique_ptr<int> mem_;
};
int main()
{
auto foo = std::make_shared<Foo>("aaa");
return 0;
}
Clang complains about the usage of a deleted constructor. To me, this makes no sense, since std::make_shared isn't supposed to copy the Foo instance, the only thing that would trigger the call to the (deleted) copy constructor of std::unique_ptr.
But lo and behold, as soon as I define a move constructor explicitly, it compiles.
#include <memory>
#include <string>
class Foo
{
public:
Foo(const std::string& s)
: mem_(new int(10))
{
}
Foo(Foo&& other)
: mem_(std::move(other.mem_))
{
}
std::unique_ptr<int> mem_;
};
int main()
{
auto foo = std::make_shared<Foo>("aaa");
return 0;
}
Now, questions:
Why does it compile in the first example but not the second?
Can std::make_shared copy/move the object while constructing it?
Why does adding a move constructor fix the problem? I don't recall that adding non-default constructor should suppress an implicit move constructor.
EDIT: Checked and all examples appear to compile fine with gcc 4.5.1 (via ideone.com), I suspect it's the case of a clang/libc++ bug, but questions 2 and 3 still stand, plus I'd like to know which compiler is more "correct".
Why does it compile in the first example but not the second?
This is a libc++ bug. I am working on a fix for it now...
Can std::make_shared copy/move the object while constructing it?
No, I don't believe it can.
Why does adding a move constructor fix the problem? I don't recall
that adding non-default constructor should suppress an implicit move
constructor.
In the version of clang you're using, implicit move constructors are not implemented yet.
Update
Fixed: http://llvm.org/bugs/show_bug.cgi?id=11616
Recently I have read that it makes sense when returning by value from a function to qualify the return type const for non-builtin types, e.g.:
const Result operation() {
//..do something..
return Result(..);
}
I am struggling to understand the benefits of this, once the object has been returned surely it's the callers choice to decide if the returned object should be const?
Basically, there's a slight language problem here.
std::string func() {
return "hai";
}
func().push_back('c'); // Perfectly valid, yet non-sensical
Returning const rvalues is an attempt to prevent such behaviour. However, in reality, it does way more harm than good, because now that rvalue references are here, you're just going to prevent move semantics, which sucks, and the above behaviour will probably be prevented by the judicious use of rvalue and lvalue *this overloading. Plus, you'd have to be a bit of a moron to do this anyway.
It is occasionally useful. See this example:
class I
{
public:
I(int i) : value(i) {}
void set(int i) { value = i; }
I operator+(const I& rhs) { return I(value + rhs.value); }
I& operator=(const I& rhs) { value = rhs.value; return *this; }
private:
int value;
};
int main()
{
I a(2), b(3);
(a + b) = 2; // ???
return 0;
}
Note that the value returned by operator+ would normally be considered a temporary. But it's clearly being modified. That's not exactly desired.
If you declare the return type of operator+ as const I, this will fail to compile.
There is no benefit when returning by value. It doesn't make sense.
The only difference is that it prevents people from using it as an lvalue:
class Foo
{
void bar();
};
const Foo foo();
int main()
{
foo().bar(); // Invalid
}
Last year I've discovered another surprising usecase while working on a two-way C++-to-JavaScript bindings.
It requires a combination of following conditions:
You have a copyable and movable class Base.
You have a non-copyable non-movable class Derived deriving from Base.
You really, really do not want an instance of Base inside Derived to be movable as well.
You, however, really want slicing to work for whatever reason.
All classes are actually templates and you want to use template type deduction, so you cannot really use Derived::operator const Base&() or similar tricks instead of public inheritance.
#include <cassert>
#include <iostream>
#include <string>
#include <utility>
// Simple class which can be copied and moved.
template<typename T>
struct Base {
std::string data;
};
template<typename T>
struct Derived : Base<T> {
// Complex class which derives from Base<T> so that type deduction works
// in function calls below. This class also wants to be non-copyable
// and non-movable, so we disable copy and move.
Derived() : Base<T>{"Hello World"} {}
~Derived() {
// As no move is permitted, `data` should be left untouched, right?
assert(this->data == "Hello World");
}
Derived(const Derived&) = delete;
Derived(Derived&&) = delete;
Derived& operator=(const Derived&) = delete;
Derived& operator=(Derived&&) = delete;
};
// assertion fails when the `const` below is commented, wow!
/*const*/ auto create_derived() { return Derived<int>{}; }
// Next two functions hold reference to Base<T>/Derived<T>, so there
// are definitely no copies or moves when they get `create_derived()`
// as a parameter. Temporary materializations only.
template<typename T>
void good_use_1(const Base<T> &) { std::cout << "good_use_1 runs" << std::endl; }
template<typename T>
void good_use_2(const Derived<T> &) { std::cout << "good_use_2 runs" << std::endl; }
// This function actually takes ownership of its argument. If the argument
// was a temporary Derived<T>(), move-slicing happens: Base<T>(Base<T>&&) is invoked,
// modifying Derived<T>::data.
template<typename T>
void oops_use(Base<T>) { std::cout << "bad_use runs" << std::endl; }
int main() {
good_use_1(create_derived());
good_use_2(create_derived());
oops_use(create_derived());
}
The fact that I did not specify the type argument for oops_use<> means that the compiler should be able to deduce it from argument's type, hence the requirement that Base<T> is actually a real base of Derived<T>.
An implicit conversion should happen when calling oops_use(Base<T>). For that, create_derived()'s result is materialized into a temporary Derived<T> value, which is then moved into oops_use's argument by Base<T>(Base<T>&&) move constructor. Hence, the materialized temporary is now moved-from, and the assertion fails.
We cannot delete that move constructor, because it will make Base<T> non-movable. And we cannot really prevent Base<T>&& from binding to Derived<T>&& (unless we explicitly delete Base<T>(Derived<T>&&), which should be done for all derived classes).
So, the only resolution without Base modification here is to make create_derived() return const Derived<T>, so that oops_use's argument's constructor cannot move from the materialized temporary.
I like this example because not only it compiles both with and without const without any undefined behaviour, it behaves differently with and without const, and the correct behavior actually happens with const only.
I want to create a class in c++. This class must manage with a collection.
OK, no problem, I would like to use operator[] of course but, in this case, my wish is to index not by position, but by name ==> that means using a string indexer.
It seems that something of this kind is not so nice to my compiler:
// In hpp
class myclass {
...
...
std::string operator[](const std::string& name);
}
// In cpp
std::string myclass::operator[](const std::string& name) {
...
}
// In main
myclass m;
std::string value = m["Name"];
Compiler tells me that he cannot solve this because operator[const char[5]] does not exists.
OK OK
I could figure this...
Compiler thinks that by calling m["Name"] I'm trying to call an operator admitting a char* and not a string... ok
Let's change the code with operator[] allowing a char* as parameter... nothing.
Can somebody tell me how to achieve such a result in c++ in a best practice way? I suppose that is a common problem to index by string and not by integer...
Thank you.
The code that you have provided should compile okay (providing the operator is public and you terminate your class declaration with a ;). I suspect the compiler error is somewhere else.
Personally, I would use std::map<std::string, std::string> as the container class.
#include <string>
#include <map>
#include <assert.h>
int main()
{
std::map<std::string, std::string> m;
m["Foo"] = "Bar";
m["Fez"] = "Baz";
assert(m["Foo"] == "Bar");
assert(m["Fez"] == "Baz");
}
It should work fine. See this example which compiles and works ok for me:
#include <iostream>
#include <string>
class MyClass
{
public:
std::string operator[] (const std::string& key) { std::cout << key << std::endl; return key; }
};
int main()
{
MyClass obj;
std::string s = obj["50"];
std::cout << s << std::endl;
}
And I see no reason it should not, since std::string has implicit constructor taking const char* so the conversion should be automatic.
Edit: From the comment it seems your problem was with your main beeing like this:
int main()
{
MyClass obj();
std::string s = obj["50"];
std::cout << s << std::endl;
}
The reason:
An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
[ Note: since () is not permitted by the syntax for initializer,
X a ();
is not the declaration of an object of class X, but the declaration of a function taking no argument and returning an X.
The form () is permitted in certain other initialization contexts (5.3.4, 5.2.3, 12.6.2). — end note ]