C++ Rule 3574 does not check if the explicit return type can actually be made implicit

Noticed using latest version of SonarLint for Visual Studio (4.13.0.11687)

C++ Rule 3574 warns about not using explicit return type for lambdas. That’s great, unless you have a lambda which actually requires an explicit return type because detection is impossible. Below you find one example of this. In this case, you could avoid this by explicitly constructing the Login object instead, but this is just one example of the general problem:

 const Login DbUser = [this]() -> Login {
		if (someCondition()) {
			return m_InitialUserData.DBUser;
		}
		return {
			GetUsernameFromSomePlace(), GeneratePassword()
		};
	}();

If you would remove -> Login in this example, the compile would fail. In VS: “a brace-enclosed list does not provide a return type for this lambda”.

As said though, this is just one example, there are plenty of reasons why auto-detection of a return type does not work. This rule only makes sense if the analyzer actually checks if the excplit trailing return type can be dropped.

Hello @awjan,

While I agree in your example Lambda return type deduction will not work as-is. I still believe it is a good practice to force the lambda return type to be deduced. As you suggested, this can be done by writing:
return Login { GetUsernameFromSomePlace(), GeneratePassword() };

Why:

The main reason why you should prefer to force return type deduction is to avoid implicit type conversion. Deduced return type doesn’t allow implicit type conversion.
For example, let’s not force type deduction and write the return type explicitly:

class AnyClass{
public:
operator Login(){...}
};

const Login DbUser = [this]() -> Login {
   	if (someCondition()) {
   		return AnyClass {}; // Implicit conversion allowed
   	}
   	return { GetUsernameFromSomePlace(), GeneratePassword() }; 
   }();

In this case, implicit conversion from AnyClass to Login is valid. No Complation error.

On the other hand, if we force type deduction:

class AnyClass{
public:
operator Login(){...}
};

const Login DbUser = [this]() {
   	if (someCondition()) {
   		return AnyClass {}; // Compilation error
   	}
   	return Login { GetUsernameFromSomePlace(), GeneratePassword() }; 
   }();

Implicit conversion in this case from AnyClass to Login is invalid. Complation error.

Let me know if you have further questions.

Abbas

What about if the return type is a reference?

const auto &returnedRef = [&]() -> const Type & {
    if (exists()) return referenceToExisiting();
    return elements.emplace_back();
}();

Also, it might be considered acceptable to allow implicit conversions from int and std::nullopt_t to std::optional<int>. One might even go as far as to say that if implicit conversions are to be avoided, then they should be avoided at the class level. So in the above example, the implicit conversion should be avoided by marking the conversion operator from AnyClass to Login as explicit. If that class is not your code, then clearly the author of the code intended implicit conversions to be allowed.

Either way. Maybe it would be a good alternative to have one rule that flags all cases of explicit lambda returns and one rule that flags explicit return types iff they are not redundant.

@torgeir.skogen, I created this ticket to add a rule that detects only redundant cases.

For reference types, I think they should be considered as an exception especially if the return type is deduced. For example, when auto& is specified as an explicit return type. The ticket to improve the existing rule.

Feel free to watch the tickets for updates and thanks for your contribution.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.