KDAB/KDAlgorithms

transform compile error in Visual Studio

hmoffatt opened this issue · 11 comments

I'm using transformed to convert a QVector into a QJsonArray of QJsonObjects as per the code below. It compiles OK with g++ 10 and clang++ 11, but not in Visual Studio 2022.

#include <kdalgorithms.h>

#include <QVector>
#include <QJsonArray>
#include <QJsonObject>

void f()
{
	struct S { int a, b; };
	QVector<S> s;

	auto j = kdalgorithms::transformed<QJsonArray>(s, [](const auto& v)
		{
			return QJsonObject{
				{ QStringLiteral("a"), v.a },
				{ QStringLiteral("b"), v.b }
			};
		});
}

The VS compiler reports

1>test.cpp(973,30): error C2039: 'a': is not a member of 'QJsonValue'
1>C:\qt\Qt5.15.12\5.15.12\msvc2019_64\include\QtCore\qjsonvalue.h(59,21): message : see declaration of 'QJsonValue'
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1470,1): message : see reference to function template instantiation 'auto f::<lambda_1>::operator ()<QJsonValue>(const _T1 &) const' being compiled
1>        with
1>        [
1>            _T1=QJsonValue
1>        ]
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1675,38): message : see reference to alias template instantiation 'std::_Decltype_invoke_nonzero<f::<lambda_1>,QJsonValue,>' being compiled
1>...\include\kdalgorithms\bits\transform.h(82,19): message : see reference to variable template 'const bool is_invocable_v<`f'::`2'::<lambda_1>,QJsonValue>' being compiled
1>test.cpp(974,30): error C2039: 'b': is not a member of 'QJsonValue'
1>C:\qt\Qt5.15.12\5.15.12\msvc2019_64\include\QtCore\qjsonvalue.h(59,21): message : see declaration of 'QJsonValue'
1>test.cpp(972,22): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'QJsonObject'
1>test.cpp(972,22): message : No constructor could take the source type, or constructor overload resolution was ambiguous

However if I replace the const auto& v parameter in the lambda with const S& v then it does compile.

It seems that with auto a QJsonValue is being passed, being the output container's value_type perhaps?

This is indeed odd. Looking at the code on ...\include\kdalgorithms\bits\transform.h(82,19), I see that it is evaluating the requirements of this function:

template <typename InputContainer, typename Transform>
auto transformed(InputContainer &&input, Transform &&transform)
#if __cplusplus >= 202002L
requires std::is_invocable_v<Transform, ValueType>
#endif

but the type you provide as the first template argument (QJsonArray) would make the first argument provided (QVector< S >) not match in the first place, and it should discard this version.

Sorry, this is beyond me.

It works with the compiler set to c++17, but not c++20 or c++2a, which confirms that code is firing. Although I don't understand why it would compile just by removing that is_invocable_v test.

But why is it trying that version of transformed() anyway? It should be the version at line 91 with a different ResultContainer type, since I have explicitly specified a different result container than the input container.

This change has not solved my Visual Studio compile errors. The problem cases are all when the output container is QJsonArray.

Adding the /Zc:__cplusplus compile flag fixes the build for me. See here for more details.

PR #47 will make adding the flag unnecessary once merged.

Adding the /Zc:__cplusplus compile flag fixes the build for me. See here for more details.

I'm afraid it doesn't fix it for me - the code in my report still does not compile. (In fact, qmake in Qt 5.15.12 already enabled that switch for me.)

However if I replace the const auto& v parameter in the lambda with const S& v then it does compile.

It also works if I specify the return type of the lambda explicitly:

void f()
{
	struct S { int a, b; };
	QVector<S> s;

	auto j = kdalgorithms::transformed<QJsonArray>(s, [](const auto& v) -> QJsonObject
		{
			return QJsonObject{
				{ QStringLiteral("a"), v.a },
				{ QStringLiteral("b"), v.b }
		};
		});
}

I also have a similar problem using filtered_transform; weirdly, I can use auto for the transform but not the filter. This compiles:

	class ContentItem
	{
	public:
		int num = 0;
		int attempt_num = 0;
		QDateTime purchased;
		QDateTime expires;
	};

	QVector<ContentItem> user_courses;

	auto x = kdalgorithms::filtered_transformed<std::vector<int>>(user_courses,
		[](const auto& item) { return item.num; },
		[expiry](const ContentItem& item) { return item.expires.isNull() || item.expires >= expiry; });

But using auto in the filter makes it fail:

	auto x = kdalgorithms::filtered_transformed<std::vector<int>>(user_courses,
		[](const auto& item) { return item.num; },
		[expiry](const auto& item) { return item.expires.isNull() || item.expires >= expiry; });

with error:

1>test.cpp(2685,44): error C2228: left of '.expires' must have class/struct/union
1>test.cpp(2685,44): message : type is 'const _T1'
1>        with
1>        [
1>            _T1=int
1>        ]
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1470,1): message : see reference to function template instantiation 'auto TUser::currentCourseLicences::<lambda_2>::operator ()<_Ty1>(const _T1 &) const' being compiled
1>        with
1>        [
1>            _Ty1=int,
1>            _T1=int
1>        ]
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1675,38): message : see reference to alias template instantiation 'std::_Decltype_invoke_nonzero<TUser::currentCourseLicences::<lambda_2>,int,>' being compiled
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1748,42): message : see reference to alias template instantiation 'std::_Is_invocable_r_<bool,TUser::currentCourseLicences::<lambda_2>,int>' being compiled
1>include\kdalgorithms\bits\shared.h(74,10): message : see reference to variable template 'const bool is_invocable_r_v<bool,`TUser::currentCourseLicences'::`2'::<lambda_2>,int>' being compiled
1>include\kdalgorithms\bits\transform.h(196,14): message : see reference to variable template 'bool UnaryPredicateOnContainerValues<`TUser::currentCourseLicences'::`2'::<lambda_2>,std::vector<int,std::allocator<int> > >' being compiled

Again adding the return types for the lambda makes it compile:

	auto x = kdalgorithms::filtered_transformed<std::vector<int>>(user_courses,
		[](const auto& item) -> int { return item.num; },
		[expiry](const auto& item) -> bool { return item.expires.isNull() || item.expires >= expiry; });

Indeed, the issue with /Zc:__cplusplus was there but it was unrelated.

I managed to reproduce your error with /std:c++latest (but not with /std:c++20 somehow).

My impression is that when concepts are introduced that error is legitimate because the requirement that the lambda is invocable with the valuetype or QJsonArray is met (type-wise auto can be replaced with QJsonValue just fine, and the body of the lambda is not taken into account with std::is_invocable). That would explain why it does work when you switch from auto to S.

Will have to dig a bit more to figure out more details.

I managed to reproduce your error with /std:c++latest (but not with /std:c++20 somehow).

Odd, I have only /std:c++20, not c++latest. Visual Studio 17.4.4 (2022).

Is there another change other than the /Zc:__cplusplus switch? Because as I wrote above I still have the problem and I thought you were able to reproduce it also.