I have a (very) simple templated type that lets me return a "IsValid" flag when returning from functions. It goes as follows:
template <typename T>
struct Validated
{
private:
T m_value;
bool m_isValid;
public:
Validated() : m_value(), m_isValid(false) {}
explicit Validated(T const& value) : m_value(value), m_isValid(true) {}
explicit Validated(bool isValid, T const& value) : m_value(value), m_isValid(isValid) {}
explicit Validated(bool isValid, T&& value) : m_value(value), m_isValid(isValid) {}
bool IsValid() const { return m_isValid; }
T const& Value() const { return m_value; }
};
Maybe there's something I don't understand with the explicit
specifier, but I was wondering why the following works just fine, and how could I avoid the conversion from bool to double?
void someFunc()
{
Validated<double> foo(1.0); // this makes perfect sense
Validated<double> bar(true); // works... (sets m_value to 1.0)
}
Been looking at similar questions/answers but couldn't find any that is satisfactory. I am aware std::optional
exists but we're not into c++17 yet. Tried this on VS2012/v110.
Update: as suggested, deleting the constructor for bool does the job (starting from c++14). It does not work for c++11 (VS2012/toolset v110).
You can simply delete the constructor taking a single bool
:
Validated(bool value) = delete;
Note: You may need some extra precautions if you want Validated<bool>
to be a valid type.
You can also prevent construction from any type other than T
(stronger than the previous one):
template <class U>
Validated(U) = delete;
This will work even with Validated<bool>
because construction from T
will match your Validated(T const&)
overload while construction from any type other than T
will match the deleted template.
This method would prevent (even explicit) construction of Validated<double>
from 1
, 1f
, etc., so you may not want to use it.
explicit
does not make your code ill-formed, it prevents implicit construction of a Validated<T>
from a T
, eg:
void f(Validated<double>);
f(1.0); // ill-formed because the conversion would be implicit
You could disable the Validated(bool) constructor in all cases apart from when T=bool Something like...
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
struct Validated
{
private:
T m_value;
bool m_isValid;
public:
Validated() : m_value(), m_isValid(false) {}
explicit Validated(T const& value) : m_value(value), m_isValid(true) {}
template <typename Y=T,typename std::enable_if<!std::is_same<Y,bool>::value,int>::type =0>
explicit Validated(bool const& value) = delete;
explicit Validated(bool isValid, T const& value) : m_value(value), m_isValid(isValid) {}
explicit Validated(bool isValid, T&& value) : m_value(value), m_isValid(isValid) {}
bool IsValid() const { return m_isValid; }
T const& Value() const { return m_value; }
};
int main() {
Validated<bool> v(true);
//Validated<int> v2(true); //fails
Validated<int> v2(2);
return 0;
}
The problem here is that argument passed to the constructor can be implicitly converted to constructor argument type. To prevent this you can convert constructor into a template and check that type of argument exactly matches template parameter:
template<typename TT> explicit
Validated(TT const& value) : m_value{value}, m_isValid{true}
{
static_assert
(
::std::is_same_v<TT, T>
, "constructor argument type should match template parameter"
);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.