Usually when a function returns boost::optional I've seen a lot of people returning an empty brace {} to designate an empty value, that works fine and is shorter than returning boost::none.
I tried to do something similar to empty a boost::optional<int>, but when calling the copy assignment operator (or most probably the move assignment op) with an empty brace in the right side, the empty brace is converted to an int and then that value is assigned to the optional, so I end up with the variable set to 0 and not an empty value as I was expecting. Here's an example https://godbolt.org/g/HiF92v, if I try the same with std::experimental::optional I get the result I'm expecting (just replace with std::experimental::optional in the example and you will see that the instruction becomes mov eax, eax).
Also if I try with a different template argument for the boost optional (a non integer type) some compilers compile (with the behavior I'm expecting, an example here http://cpp.sh/5j7n) and others don't. So even for the same lib the behavior is different according to the template arg.
I'd like to understand what is going on here, I know it has something to do with the fact that I'm using a C++14 feature for a library that doesn't consider that into the design. I read the boost/optional header but I got lost in the details, I also tried to study the compiled code without inlining with a similar result.
I'm using gcc 4.9.2 with -std=c++14 and boost 1.57.
btw: I known I should have used boost::optional::reset or boost::none, but I was trying to be consistent with the semantics in the rest of the code base.
To understand what is going on, consider this example first:
void fun(int) { puts("f int"); }
void fun(double) { puts("f double"); }
int main() {
fun({}); // error
}
This results in a compiler error, because the overload resolution is inconclusive: double and int fit equally well. But, if a non-scalar type comes into play, the situation is different:
struct Wrap{};
void fun(int) { puts("f(int)"); }
void fun(Wrap) { puts("f(Wrap)"); }
int main() {
fun({}); // ok: f(int) selected
}
This is because a scalar is a better match. If, for some reason, I want the same two overloads but at the same time I would like fun({}) to select overload fun(Wrap), I can tweak the definitions a bit:
template <typename T>
std::enable_if_t<std::is_same<int, std::decay_t<T>>::value>
fun(T) { puts("f int"); }
void fun(Wrap) { puts("f(Wrap)"); }
That is, fun(Wrap) remains unchanged, but the first overload is now a template that takes any T. But with enable_if we constrain it, so that it only works with type int. So, this is quite an 'artificial' template, but it does the job. If I call:
fun(0); // picks fun(T)
The artificial template gets selected. But if I type:
fun({}); // picks fun(Wrap)
The artificial template is still a template, so it is never considered in type deduction in this case, and the only visible overload is fun(Wrap), so it gets selected.
The same trick is employed in std::optional<T>: it does not have an assignment from T. Instead it has a similar artificial assignment template that takes any U, but is later constrained, so that T == U. You can see it in the reference implementation here.
boost::optional<T> has been implemented before C++11, unaware of this 'reset idiom'. Therefore it has a normal assignment from T, and in cases where T happens to be a scalar this assignment from T is preferred. Hence the difference.
Given all that, I think that Boost.Optional has a bug that it does something opposite than std::optional. Even if it is not implementable in Boost.Optional, it should at least fail to compile, in order to avoid run-time surprises.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
AFAIK overloading a function for types that are relative by conversion or a function call that needs some cast applied to the argument to match a best a match are of bad design.
void foo(int)
{
std::cout << "foo(int)\n";
}
void foo(float)
{
std::cout << "foo(float)\n";
}
int main()
{
foo(5.3);// ambiguous call
foo(0u); // ambiguous call
}
Because 5.3 is of type double so it can be equally converted to either float or int thus there are more than best match for the call consequently the call is ambiguous. In the second call the same thing: 0u is of type unsigned int which can be converted to int or float equally thus the call is ambiguous.
To disambiguate the calls I can use a an explicit cast:
foo(static_cast<float>(5.3)); // calls foo(float)
foo(static_cast<int>(0u)); // calls foo(int)
The code now works but it is a bad design because that breaks the principle of Function overloading where the compiler is the responsible for choosing the best match function for a call depending on the arguments passed it.
Until here I'm OK. But what about templates argument deduction? :
*The compiler applies only a few conversions on the arguments passed in to function template call to deduce the type of template arguments.
So the compiler doesn't apply arithmetic conversion nor integral promotion but instead it often generates a new version that best math the call:
template <typename T>
void foo(T)
{
std::cout << "foo(" << typeid(T).name() << ")\n";
}
int main()
{
foo(5.3); // calls foo(double)
foo(0u); // calls foo(unsigned)
}
Now it works fine: the compiler generates two versions of foo one with double and the second with unsigned.
The thing that matters me: Is it a bad idea to pass arguments of related types into a function template that uses template argument deduction for its arguments?
Or the problem is in the language itself? Because the compiler generates versions that can be relative by conversion?
In your first program containing the overload set of foo with int and float, the language rules say that calls like:
foo(5.3);
foo(0u);
are ambiguous. Overload resolution rules say that in both those calls, the conversions needed for the function arguments to match the parameters are tied, resulting in both candidates being equally good matches.
Your solution using a single function template could work, depending on your use case. One potential issue is that every single call to foo with a different argument type will result in a completely different instantiation of foo. Another is that this function template will accept any argument type for which the definition of foo is valid. Neither of these behaviors may be what you desire.
Based on your overload set in the first program, and the static_casts in your code to explicitly call specific versions, it seems that you actually want to group int and unsigned, i.e integral types into one category, and float and double, i.e. floating point types into another. You could implement this in a number of different ways, e.g. by providing an exhaustive overload set that covers all the types that you care about.
The approach I would recommend is to implement the overload set with two function templates, each allowing one family of types. From C++20, you could implement it like this:
void foo(std::integral auto)
{
std::cout << "foo(integral)\n";
}
void foo(std::floating_point auto)
{
std::cout << "foo(floating_point)\n";
}
You can also achieve the same effect before C++20, with a little more syntax, and using SFINAE and other template meta-programming techniques.
Here's a demo.
Or the problem is in the language itself? Because the compiler generates versions that can be relative by conversion?
Okay, they are related by conversion. Now which one should the compiler generate? What deterministic algorithm do you propose it employ to choose the best function to instantiate? Should it parse the entire translation unit first to figure out the best match, or should it still parse top to bottom and keep a "running best" function?
Now, assuming those questions are answered. What happens when you modify your code a bit? What happens if you include a header that instantiates an even better function? You didn't really change your code, but its behavior is still altered, possibly very drastically.
Considering the design headache it is for the language, and the potential chaos this behavior can bring unto unsuspecting code, it'd be a very bad idea to try and make compilers do this.
So no, it's not a language problem. The current behavior is really the sanest choice, even if it's not always what we want, it's something we can learn to expect.
The thing that matters me[sic]: Is it a bad idea to pass arguments of related types into a function template that uses template argument deduction for its arguments?
There's no way to answer it generally for all cases. There are no silver bullets. It could be exactly what your overload set needs to do. Or it could be that you need to build a more refined set of function(s) (templates) that interact with overload resolution via SFINAE or more modern techniques. For instance, you could do this in C++20
template <std::integral I>
void foo(I)
{
//
}
template <std::floating_point F>
void foo(F)
{
//
}
The concepts constrain each template to work only with a specific family of types. That's one way to build the overload set you wanted in your first example, avoid the ambiguity, and work with exact types as templates are designed.
If I have a function template that has default argument for its template parameter and that function takes a non-default parameter of the type parameter then what's the point in the language to allow that default argument that'll never be used? :
template <class T = int>
void foo(T x){cout << x << endl;}
int main()
{
foo("hi"); // T is char const *
foo(); // error
}
As you can see T=int can never be used because the function doesn't have a default parameter thus the compiler in this context always deduces the type of T from the argument passed to foo.
But it can be used. Here's an example.
auto* foo_ptr = &foo<>; // The default template argument is used.
A function call expression is not the only context where a function template's arguments need to be figured out.
Although default parameters are usually used for non deduced parameters, taking the address of the function (&foo) uses them too.
Another example:
#include <typeinfo>
#include <iostream>
using namespace std;
template <class T = int>
void coutType() {
cout << typeid(T).name() << endl;
}
int main() {
// default
coutType();
// non-default
coutType<double>();
}
Output with Clang++
int
double
what's the point in the language to allow [X]?
Better: what would be the point in prohibiting [X]?
There is value in simplicity. I would give the burden of proof to the side that wants to make the language more complicated. The language allows a template parameter to have a default value. The language allows a template parameter to be deduced when the function is directly invoked. It is simpler to allow these to co-exist than to add a prohibition against using both. Hence I would ask why prohibit, rather than ask why allow. If there is no compelling reason for the complication, then stick to simple. Being allowed to do something does not force one to do it. And maybe someone (like StoryTeller and Dani) will find a use for that something.
Of course, simplicity is not the ultimate criterion. If harm would come from [X], then that would likely outweigh simplicity concerns. Complications can be justified. However, complications should not be introduced just because something seems useless.
On the other hand, one could reasonably ask if [X] can be put to use. And maybe that was the real question, even if the OP did not realize it. Still, I thought I would put up one answer addressing the question-as-phrased.
Consider a function template func that is very performance critical. It can be instantiated with T=Type1 or some other type. Part of the function logic depends on T it is instantiated with.
One can either explicitly use a if constexpr (Code B) or use a vanilla if instead (Code A), while compiler probably optimizes the code.
However, I wonder, how the implementation without constexpr (Code A) is any different? Isn't the compiler capable of detecting which branch of if (in Code A) to use at compile time while instantiating? Can it still (for Code A) generate a less efficient code?
Code A. Without if constexpr:
template<class T>
void func(T argument)
{
// some general type-independent logic
if (std::is_same<Type1,T>::value)
{
// do something
}
else
{
// do something else
}
// some general type-independent logic
}
Code B. With if constexpr:
template<class T>
void func(T argument)
{
// some general type-independent logic
if constexpr (std::is_same<Type1,T>::value)
{
// do something
}
else
{
// do something else
}
// some general type-independent logic
}
Both codes A & B compile, as do something and do something else are well-formed for any T.
There are some similar-sounding questions:
Why is constexpr if needed? – this one answers when constexpr is required.
Difference between if and constexpr if – just lists the differences
The aforementioned questions do not answer if Code B is preferable to Code A for some reason (when both branches are well-formed anyway).
The only advantage I see would be to tell the programmer explicitly that this if is compile-time; however, I would say the conditional expression is self-explanatory.
if constexpr is not intended about optimization. Compilers are very good at optimizing away a branch that is if (true) or if (false) (since we're talking about constant expressions, that is what it boils down to). Here is a godbolt demo of the example in OP - you'll note that both gcc and clang, even on -O0, do not emit a branch for a simple if.
if constexpr is all about ensuring that only one branch of the if is instantiated. This is hugely important and valuable for writing templates - because now we can actually write conditionally compiling code within the body of the same function instead of writing multiple artificial functions just to avoid instantiation.
That said, if you have a condition that is a known constant expression - just always use if constexpr, whether or not you need the instantiation benefit. There is no downside to such a decision. It makes it clearer to readers that indeed this condition is constant (since otherwise it wouldn't even compile). It will also force the evaluation of the expression as a constant (a slight variant leads gcc to emit a branch at -O0, thought not at -O1), which with the coming addition of is_constant_evaluated() may become more important in the long run (possibly even negating my opening paragraph).
The only advantage I see would be to tell the programmer explicitly that this if is compile-time; however, I would say the conditional expression is self-explanatory.
To address this specifically, yes, std::is_same<X, Y>::value is "self-explanatory" that it is a constant expression... because we happen to be familiar with std::is_same. But it's less obvious whether foo<X>::value is a constant expression or whether foo<X>() + bar<Y>() is a constant expression or anything more arbitrarily complicated than that.
It's seeing if constexpr that makes the fact that it's compile-time self-explanatory, not the content of the condition itself.
Adding an example to #Barry 's explanation: The use is primarily for writing templates. Consider the following:
template <class T>
auto get_value()
{
if constexpr (std::is_same_v<T, int>) {
return 1
} else {
return 2.0;
}
}
You can note that, if the template parameter is int, the return value is determined to be int, while it is float when the template parameter is not int. You will see that this does not work with non-constexpr if statements, because at instantiation, all returns of a function must have a common type, which the former does not have. The only other way of achieving this is to use c++20 contraints, or std::enable_if to overload the function based on the template parameter.
Consider the following function :
template <typename Type>
void f(const Type& x)
I would like to do something special (without specialization) in it whether the passed type is an empty std::tuple or an empty std::array. For a tuple of nu elements, I can use std::is_same<Type, std::tuple<>>::value but what trick can I use to detect an zero-element array ?
(I am searching for a solution that do not require the creation of another function or class, ...)
You can use std::tuple_size, as it will also work for std::array! See here. Simply use:
std::tuple_size<Type>::value == 0
to check if Type is an empty std::tuple<> or an empty std::array<T,0>.
With the above, the question remains what happens if Type is neither a std::tuple not a std::array. The general approach I see is this:
constexpr bool IsNotTupleOrArray =
!std::is_class<Type>::value ||
std::is_same<Type,ExcludeThisClass>::value ||
sizeof(Type)>1 || // non-portable, works for GCC 4.8+
...;
std::conditional< IsNotTupleOrArray,
std::false_type,
std::tuple_size<Type> >::type::value;
which basically means that you have to explicitly exclude other types. For example is_class<Type> excludes all fundamental types like int, pointers, etc.
If you don't want to do it without specialization, why not doing it with overload? ;) Specializing a function template is sometimes (no, often) a bad idea. With some SFINAE trick it wouldn't be difficult to create an overload that is selected only when those two conditions apply.
However, I already hear you shouting that it's not what you wanted to do: what you wanted is some kind of if inside of f() that would be executed in case the condition is true, and a corresponding else branch that would be executed when the condition is false.
However, notice that this would not be a static if, but a regular run-time if: in other words, the compiler would know at compile-time with 100% certainty that one of those two branches is never going to be executed (and it would probably issue an annoying warning about it), but it will have to parse both branches and prove them legal.
In practice, this means that you won't be able to put statements that depend on the particular type T (or on properties of T) in order to be compilable. For instance, in the code below compile_time_expression determines whether type T has a member function foo() or a member function bar():
T obj;
if (compile_time_expression)
{
obj.foo();
}
else
{
obj.bar();
}
However, the above won't compile if that particular T doesn't have both a member function foo() and a member function bar(): since what you have here is a run-time if, the compiler will have to parse both branches and make sure they're both compilable - and then possibly optimize away the one that is never going to be executed.
Unfortunately, C++ does not have any construct such as static if (yet?), so overloading + SFINAE is the right way to tackle this problem.
There was a question regarding passing taking the following function template, and instantiating it with a by-reference string parameter:
template <typename T>
void foo(T t) {}
The answer was, of course, to give the argument explicitly:
int main() {
std::string str("some huge text");
foo<std::string&>(str);
}
(As an aside, there was a suggestion about using deduction and passing C++0x's std::ref(str), but this requires the use of .get() inside the function, and the OP's requirement was transparency.)
However, IMO there can be little doubt that the author of the function template intended for the argument to be passed by value, or he would have written:
template <typename T>
void foo(T& t) {}
(It's possible that he deliberately intended for either to be possible, but this seems unlikely to me for some reason.)
Are there any "reasonable" scenarios where passing a reference into foo<T>, where the author of foo had intended the function always to take its argument by value, may cause problems?
The answer I'm looking for most likely consists of a function body for foo, where the use of a reference type for T leads to unexpected/undesirable semantics when compared to the use of a value type for T.
Consider that it's common to write algorithms like this:
template <typename InputIterator>
void dosomething(InputIterator first, InputIterator last, otherparams) {
while (first != last) {
do the work;
++first;
}
}
It's also how the standard algorithms are implemented both in GCC and in the old SGI STL.
If first is taken by reference then it will be modified, so certainly there are plenty of template functions out there that will "go wrong" with reference template arguments. In this example, first is changed to a value equal to last. In another implementation, or in the next release, it might be copied and not modified at all. That's "unexpected/undesirable semantics".
Whether you call this a "problem" or not, I'm not sure. It's not the behavior that the author of the function intended, and it's probably not documented what happens to first if it's passed by reference (it isn't for the standard algorithms).
For the standard algorithms I think it's UB, so the caller is at fault. The standard says that the template argument should be an iterator, and while T* or some library iterator is an iterator type, T* & or reference-to-library-iterator isn't.
As long as authors of template functions have documented the requirements of their template arguments clearly, I suspect that typically it will just fall out in the same way as for standard algorithms and iterators -- reference types are not valid template arguments, and hence the caller is at fault. In cases where the requirements are very simple (a couple of expressions with specified behavior), a reference type probably isn't ruled out, but again as long as the function doesn't say that it doesn't modify the argument, and doesn't say how it modifies the argument, callers should consider that since it's not documented how the argument is modified, then it's unspecified. If they call the function with a reference type, and get surprised whether or how the argument is modified, again it's the caller's fault.
I expect that under-documented functions are at risk of a dispute whose fault it is when it goes wrong, though, since sometimes it'll rely on quite a close reading.
If the programmer decided to be lazy, and use the input parameter as a temporary variable to calculate the output, then you would get problematic results:
template <class T>
T power2n(T a, int n) {
if (n == 0) return 1;
for (int i = 0 ; i < n; i++)
{
a *= a;
}
return a;
}
Now if I pass the first parameter by reference, its value gets messed up.
Not saying this is good programming, just saying it happens.