Date: Sat, 16 Apr 2022 22:12:50 +0900
Hello,
I have been following std-proposals for a little while and would like to
share my first idea with everyone. It's not fully formalized yet but I
would be grateful for some initial feedback on it.
What I would like to propose is a new syntax for deducing either a value
type or a const reference type depending on whether the type being deduced
is both a) trivially copyable and b) two words in size or fewer.
I think that auto? would be an intuitive syntax for this, as it suggests
that the type may or may not be deduced as a reference type. Essentially
auto? is the same as auto when the two conditions above are met, and then
same as const auto& when not.
Here's what auto? would look like in different scenarios:
// local variable declaration
auto? t = vector.front();
// range-based for loop
for (auto? i : vector) {}
// lambda argument
auto lambda = [](auto? x){};
Has this sort of language feature been suggested before? If so, can someone
point me to the paper for it?
I am aware of the call_traits library
<https://www.boost.org/doc/libs/master/libs/utility/doc/html/utility/utilities/call_traits.html>
in boost that offers similar functionality, but as far as I understand,
call_traits results in all user-defined types being passed by reference,
whereas I am proposing that even user-defined types can be passed by value
as long as they meet the requirements.
I am also aware of clang-tidy's performance-unnecessary-value-param
<https://clang.llvm.org/extra/clang-tidy/checks/performance-unnecessary-value-param.html>,
which basically enforces C++ Core Guidelines F.16
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f16-for-in-parameters-pass-cheaply-copied-types-by-value-and-others-by-reference-to-const>
.
The boost library and clang-tidy check are useful, but I believe that
promoting this concept to a language-level feature will ultimately be most
beneficial to programmers as it both provides a more terse syntax and also
is more robust to refactoring than choosing between auto and auto&.
To illustrate what I mean, consider the following program that implements
the auto? idea - the pass_by_t type resolves to a value type for
non-trivially copyable types and for types larger than twice the word size,
and a const reference type otherwise.
#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>
template<typename T>
using pass_by_t =
std::conditional_t<std::is_trivially_copyable_v<std::decay_t<T>> &&
sizeof(std::decay_t<T>) <= 2 * sizeof(void*), T, const T&>;
struct a { void* m1; };
struct b { void* m1; void* m2; };
struct c { void* m1; void* m2; void* m3; };
int main() {
std::cout << "one member: " << std::is_reference_v<pass_by_t<a>> <<
std::endl;
std::cout << "two members: " << std::is_reference_v<pass_by_t<b>> <<
std::endl;
std::cout << "three members: " << std::is_reference_v<pass_by_t<c>> <<
std::endl;
std::cout << "std::string: " <<
std::is_reference_v<pass_by_t<std::string>> << std::endl;
std::cout << "std::string_view: " <<
std::is_reference_v<pass_by_t<std::string_view>> << std::endl;
}
$ g++-11 main.cpp
$ ./a.out
one member: 0
two members: 0
three members: 1
std::string: 1
std::string_view: 0
If more members are added to a class over time then auto? will seamlessly
switch from pass by value to pass by const reference. Without auto? the
choice would be between auto and auto& and refactoring may be required to
pass objects in the most efficient way as the size of the object grows over
time.
In the program above I also show that std::string would be passed as a
const reference type and std::string_view would be passed as a value type,
which is consistent with the current best practice.
In terms of drawbacks to this idea, I can think of at least two. The first
one is that it might be a bit hard to understand whether auto? deduces a
value type or a reference type in certain situations, but if in doubt the
programmer can always stick to using the tried-and-true auto or auto&
instead.
The second potential drawback is that the two times word size limit is a
bit arbitrary, and there might be those who want the limit to be one word
or three words or something else. Perhaps there is a better way to
standardize the limit based on the platform, or perhaps it can be left up
to vendors to decide.
Lastly, there are plenty of other things to work out, such as how to deal
with array types, but I figured I would get some initial feedback first.
Please let me know whether you think this idea is worth pursuing further.
Jonathan Sweemer
I have been following std-proposals for a little while and would like to
share my first idea with everyone. It's not fully formalized yet but I
would be grateful for some initial feedback on it.
What I would like to propose is a new syntax for deducing either a value
type or a const reference type depending on whether the type being deduced
is both a) trivially copyable and b) two words in size or fewer.
I think that auto? would be an intuitive syntax for this, as it suggests
that the type may or may not be deduced as a reference type. Essentially
auto? is the same as auto when the two conditions above are met, and then
same as const auto& when not.
Here's what auto? would look like in different scenarios:
// local variable declaration
auto? t = vector.front();
// range-based for loop
for (auto? i : vector) {}
// lambda argument
auto lambda = [](auto? x){};
Has this sort of language feature been suggested before? If so, can someone
point me to the paper for it?
I am aware of the call_traits library
<https://www.boost.org/doc/libs/master/libs/utility/doc/html/utility/utilities/call_traits.html>
in boost that offers similar functionality, but as far as I understand,
call_traits results in all user-defined types being passed by reference,
whereas I am proposing that even user-defined types can be passed by value
as long as they meet the requirements.
I am also aware of clang-tidy's performance-unnecessary-value-param
<https://clang.llvm.org/extra/clang-tidy/checks/performance-unnecessary-value-param.html>,
which basically enforces C++ Core Guidelines F.16
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f16-for-in-parameters-pass-cheaply-copied-types-by-value-and-others-by-reference-to-const>
.
The boost library and clang-tidy check are useful, but I believe that
promoting this concept to a language-level feature will ultimately be most
beneficial to programmers as it both provides a more terse syntax and also
is more robust to refactoring than choosing between auto and auto&.
To illustrate what I mean, consider the following program that implements
the auto? idea - the pass_by_t type resolves to a value type for
non-trivially copyable types and for types larger than twice the word size,
and a const reference type otherwise.
#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>
template<typename T>
using pass_by_t =
std::conditional_t<std::is_trivially_copyable_v<std::decay_t<T>> &&
sizeof(std::decay_t<T>) <= 2 * sizeof(void*), T, const T&>;
struct a { void* m1; };
struct b { void* m1; void* m2; };
struct c { void* m1; void* m2; void* m3; };
int main() {
std::cout << "one member: " << std::is_reference_v<pass_by_t<a>> <<
std::endl;
std::cout << "two members: " << std::is_reference_v<pass_by_t<b>> <<
std::endl;
std::cout << "three members: " << std::is_reference_v<pass_by_t<c>> <<
std::endl;
std::cout << "std::string: " <<
std::is_reference_v<pass_by_t<std::string>> << std::endl;
std::cout << "std::string_view: " <<
std::is_reference_v<pass_by_t<std::string_view>> << std::endl;
}
$ g++-11 main.cpp
$ ./a.out
one member: 0
two members: 0
three members: 1
std::string: 1
std::string_view: 0
If more members are added to a class over time then auto? will seamlessly
switch from pass by value to pass by const reference. Without auto? the
choice would be between auto and auto& and refactoring may be required to
pass objects in the most efficient way as the size of the object grows over
time.
In the program above I also show that std::string would be passed as a
const reference type and std::string_view would be passed as a value type,
which is consistent with the current best practice.
In terms of drawbacks to this idea, I can think of at least two. The first
one is that it might be a bit hard to understand whether auto? deduces a
value type or a reference type in certain situations, but if in doubt the
programmer can always stick to using the tried-and-true auto or auto&
instead.
The second potential drawback is that the two times word size limit is a
bit arbitrary, and there might be those who want the limit to be one word
or three words or something else. Perhaps there is a better way to
standardize the limit based on the platform, or perhaps it can be left up
to vendors to decide.
Lastly, there are plenty of other things to work out, such as how to deal
with array types, but I figured I would get some initial feedback first.
Please let me know whether you think this idea is worth pursuing further.
Jonathan Sweemer
Received on 2022-04-16 13:13:03