[parameter] why are keywords non-const?

The BOOST_PARAMETER_KEYWORD(tag, name) macro expands, in part, to something like: keyword<tag::name> & name = ...; Shouldn't this be: keyword<tag::name> const & name = ...; ^^^^^ What is the reason for using a non-const ref here? These things are not mutable. In fact, it is causing me problems. Consider a placeholder type with an implicit conversion operator: struct default_constructed_t { template<typename T> operator T() const { return T(); } }; default_constructed_t const default_constructed = {}; BOOST_PARAMETER_KEYWORD(tag, value) Now, when I do this: "value = default_constructed;" I get this error: main.cpp:306: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: /cygdrive/c/boost/consulting/svn/main/boost/1.33.1/backports/boost_1_33_1/boost/parameter/ keyword.hpp:59: note: candidate 1: const typename boost::parameter::aux::tag<Tag, const T>::type boost::parameter::keyword<T>::operator=(const T&) const [with T = default_constructed_t, Tag = tag::value] /cygdrive/c/boost/consulting/svn/main/boost/1.33.1/backports/boost_1_33_1/boost/parameter/ keyword.hpp:32: note: candidate 2: boost::parameter::keyword<tag::value>& boost::parameter::keyword<tag::value>::operator=(const boost::parameter::keyword<tag::value>&) Had the keyword object been const, the call would not have been ambiguous. -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> writes:
The BOOST_PARAMETER_KEYWORD(tag, name) macro expands, in part, to something like:
keyword<tag::name> & name = ...;
Shouldn't this be:
keyword<tag::name> const & name = ...; ^^^^^
What is the reason for using a non-const ref here?
There didn't seem to be any reason to make it const.
These things are not mutable.
If immutability is a property of the type, it wouldn't seem to be important to additionally label the object as const.
In fact, it is causing me problems. Consider a placeholder type with an implicit conversion operator:
struct default_constructed_t { template<typename T> operator T() const { return T(); } };
default_constructed_t const default_constructed = {};
BOOST_PARAMETER_KEYWORD(tag, value)
Now, when I do this: "value = default_constructed;" I get this error:
main.cpp:306: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: /cygdrive/c/boost/consulting/svn/main/boost/1.33.1/backports/boost_1_33_1/boost/parameter/ keyword.hpp:59: note: candidate 1: const typename boost::parameter::aux::tag<Tag, const T>::type boost::parameter::keyword<T>::operator=(const T&) const [with T = default_constructed_t, Tag = tag::value] /cygdrive/c/boost/consulting/svn/main/boost/1.33.1/backports/boost_1_33_1/boost/parameter/ keyword.hpp:32: note: candidate 2: boost::parameter::keyword<tag::value>& boost::parameter::keyword<tag::value>::operator=(const boost::parameter::keyword<tag::value>&)
Had the keyword object been const, the call would not have been ambiguous.
True enough. I suppose we have no interest in enabling that second overload (the implicitly generated assignment operator) for any purpose, so it makes sense to make the keywords references-to-const. Actually, now that I look at this, it seems Boost.Parameter is still using the "bad-old-method" of initializing these things. IIRC we decided that to avoid order-of-initialization problems /and/ ODR one needs a reference declared in an unnamed namespace, and initialized directly with a static const POD member of a class template, right? We're still going through a get() function. If we constify the reference we should fix this problem too. Aside: I'm not yet convinced that as a general principle, all instances of every immutable type ought to be declared const. This particular problem where we have a generalized conversion operator and a generalized assignment template looks like a corner case to me. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Actually, now that I look at this, it seems Boost.Parameter is still using the "bad-old-method" of initializing these things. IIRC we decided that to avoid order-of-initialization problems /and/ ODR one needs a reference declared in an unnamed namespace, and initialized directly with a static const POD member of a class template, right? We're still going through a get() function. If we constify the reference we should fix this problem too.
Agreed.
Aside: I'm not yet convinced that as a general principle, all instances of every immutable type ought to be declared const. This particular problem where we have a generalized conversion operator and a generalized assignment template looks like a corner case to me.
OK. I declare an object to be const when I want attempts to mutate the object to fail at compile time. IMO, it's nice to let users know when they're asking for something nonsensical (mutate an immutable object). OTOH, one could argue that letting people "mutate" an object with no state is harmless, and disallowing it could in fact hurt genericity. I haven't found that to be the case in practice, but I don't know. It's an interesting question. Also an interesting question is how to get the type system to enforce the immutability of a type. If there is a type which is inherently immutable (because it is empty, for instance), we have two options: - Const-qualify all the instances: Works, but is a hassle because you might miss a const somewhere. You could declare X_impl in a detail namespace, and make X a typedef for detail::X_impl const, but that only works if X is an actual type, not a template like keyword<>. (Template typedefs would fix this.) - Private copy-assign operator Would seem to be a better option, since it only involves a change in one location, but it would make the type non-POD (9/4), which is a deal-breaker for keyword<> since it should be statically initialized. Sorry, no answers here, just questions. :-P -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> writes:
Also an interesting question is how to get the type system to enforce the immutability of a type. If there is a type which is inherently immutable (because it is empty, for instance), we have two options:
I guess I don't get what you're trying to accomplish here. If it's inherently immutable, the compiler is enforcing immutability no matter what you do. IOW, there's no available interface through which you can mutate instances. That's just what adding const to a well-behaved mutable type does: makes all mutating operations unavailable. What am I missing? -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
"Eric Niebler" <eric@boost-consulting.com> writes:
Also an interesting question is how to get the type system to enforce the immutability of a type. If there is a type which is inherently immutable (because it is empty, for instance), we have two options:
I guess I don't get what you're trying to accomplish here. If it's inherently immutable, the compiler is enforcing immutability no matter what you do. IOW, there's no available interface through which you can mutate instances. That's just what adding const to a well-behaved mutable type does: makes all mutating operations unavailable.
What am I missing?
Terminology issue? I mean, if there is a type X, and we want "x = y" to fail to compile for objects x,y of type X, what is the best way to accomplish that? 1) X is actually a typedef for a const-qualified type detail::X_impl 2) X has a private copy-assign operator I then pointed out that neither of these are appropriate for keyword<>. -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> writes:
David Abrahams wrote:
What am I missing?
Terminology issue? I mean, if there is a type X, and we want "x = y" to fail to compile for objects x,y of type X
Is that something we want in this case?
what is the best way to accomplish that?
1) X is actually a typedef for a const-qualified type detail::X_impl
Technically not enough, because it could have a const-qualified copy-assignment operator.
2) X has a private copy-assign operator
I then pointed out that neither of these are appropriate for keyword<>.
I see. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (2)
-
David Abrahams
-
Eric Niebler