Home > Back-end >  Preventing implict construction for function arguments
Preventing implict construction for function arguments

Time:12-24

I have a class which has several constructors, one accepting a string:

AClass(const std::string& str);

and I have a function which requires an instance of AClass:

void AFunction(const AClass& a);

Question

I just inadvertently passed a completely unrelated string object, instead of an AClass but there were no compiler errors because of AClass's string constructor.

Is it possible to generate a compiler error if the function is called passing a string:

std::string s("");
AFunction(s);

because I would like it to require an actual AClass object:

AClass a;
AFunction(a);

or

AFunction(AClass(""));

CodePudding user response:

The pragmatic way is to make the constructor explicit:

class AClass {
public:
    explicit AClass(const std::string& str) {}
};

If, for some reason, you cannot touch that code, then you might resort to a ref-holder class:

#include <iostream>

// unchanged
class AClass {
public:
    AClass(const std::string& str) {}
};

// this holds a const T& value
// note: cannot be used with temporary!
template<typename T>
struct ExplicitFrom {
    ExplicitFrom(const T& t) : value(t) {}
    const T& value;
};

//instead of: void AFunction(const AClass& a) {}
void AFunction(const ExplicitFrom<AClass>& a) {}

int main() {
    std::string s;
    AClass a(s);
    AFunction(a);

    // AFunction(s); // error
}

CodePudding user response:

(Assuming that for whatever reason the class should not be modified by adding explicit.)

Since C 20:

#include<concepts>

void AFunction(const std::same_as<AClass> auto& a) { /*...*/ };

This makes the function an (abbreviated) function template, so it should be defined in the header if shared between compilation units. You can also additionally explicitly instantiate it, in which case it can still be defined only in the translation unit with the explicit instantiation:

template void AFunction(const AClass& a);

Alternatively, if you still want to allow objects of a type derived from AClass to be passed without requiring an explicit conversion, then you can use std::derived_from instead of std::same_as, but then, because you can't know the derived classes up-front, you can't use the explicit instantiation approach.

  • Related