可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a templated class that has a data member of type std::vector<T>
, where T is also a parameter of my templated class.
In my template class I have quite some logic that does this:
T &value = m_vector[index];
This doesn't seem to compile when T is a boolean, because the [] operator of std::vector does not return a bool-reference, but a different type.
Some alternatives (although I don't like any of them):
- tell my users that they must not use bool as template parameter
- have a specialization of my class for bool (but this requires some code duplication)
Isn't there a way to tell std::vector not to specialize for bool?
回答1:
You simply cannot have templated code behave regularly for T
equal to bool
if your data is represented by std::vector<bool>
because this is not a container. As pointed out by @Mark Ransom, you could use std::vector<char>
instead, e.g. through a trait like this
template<typename T> struct vector_trait { typedef std::vector<T> type; };
template<> struct vector_trait<bool> { typedef std::vector<char> type; };
and then use typename vector_trait<T>::type
wherever you currently use std::vector<T>
. The disadvantage here is that you need to use casts to convert from char
to bool
.
An alternative as suggested in your own answer is to write a wrapper with implicit conversion and constructor
template<typename T>
class wrapper
{
public:
wrapper() : value_(T()) {}
/* explicit */ wrapper(T const& t): value_(t) {}
/* explicit */ operator T() { return value_; }
private:
T value_;
};
and use std::vector< wrapper<bool> >
everywhere without ever having to cast. However, there are also disadvantages to this because standard conversion sequences containing real bool
parameters behave differently than the user-defined conversions with wrapper<bool>
(the compiler can at most use 1 user-defined conversion, and as many standard conversions as necessary). This means that template code with function overloading can subtly break. You could uncomment the explicit
keywords in the code above but that introduces the verbosity again.
回答2:
Use std::vector<char>
instead.
回答3:
Would the following work for you?
template <typename T>
struct anything_but_bool {
typedef T type;
};
template <>
struct anything_but_bool<bool> {
typedef char type;
};
template <typename T>
class your_class {
std::vector<typename anything_but_bool<T>::type> member;
};
Less flippantly, the name anything_but_bool
should probably be prevent_bool
or similar.
回答4:
There is a ways to prevent the vector<bool>
specialization: Passing a custom allocator.
std::vector<bool, myallocator> realbool;
The following article has some details:
https://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=98
Of course, this requires that you have control over the vector
definitions, so it's probably not really a solution for you. Apart from that, it also has some downsides of it's own...
回答5:
I found an even more elegant solution, based on all of your input.
First I define a simple class that holds one member. Let's call this wrapperClass
:
template <typename T>
class wrapperClass
{
public:
wrapperClass() {}
wrapperClass(const T&value) : m_value(value) {}
T m_value;
};
Now I can define my std::vector in my templated class like this:
std::vector<wrapperClass<T>> m_internalVector;
Since sizeof(WrapperClass<bool>)
is also 1, I expect that sizeof(WrapperClass<T>)
will always be equal to sizeof(T)
. Since the data type is now not a bool anymore, the specialization is not performed.
In places where I now get an element from the vector, I simply replace
m_internalVector[index]
by
m_internalVector[index].m_value
But this seems much more elegant than using traits to replace bool by char, and then using casts to convert between char and bool (and probably reinterpret casts to convert char& to bool&).
What do you think?
回答6:
You could use a custom proxy class to hold the bools.
class Bool
{
public:
Bool() = default;
Bool(bool in) : value(in) {}
Bool& operator=(bool in) {value = in;}
operator bool() const& {return value;}
private:
bool value;
};
This might require a bit of tweaking for your purposes, but it's usually what I do in these cases.