Passing functions in C++
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
Suppose I want to write a function that calls a nullary function 100 times. Which of these implementations is best and why?
template<typename F>
void call100(F f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(F& f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(const F& f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; i++)
f();
}
Or is there a better implementation?
Update regarding 4
struct S {
S() {}
S(const S&) = delete;
void operator()() const {}
};
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; i++)
f();
}
int main() {
const S s;
call100(s);
}
c++ c++17
add a comment |
Suppose I want to write a function that calls a nullary function 100 times. Which of these implementations is best and why?
template<typename F>
void call100(F f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(F& f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(const F& f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; i++)
f();
}
Or is there a better implementation?
Update regarding 4
struct S {
S() {}
S(const S&) = delete;
void operator()() const {}
};
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; i++)
f();
}
int main() {
const S s;
call100(s);
}
c++ c++17
1
go for the last one.
– Hoodi
17 hours ago
5
@Hoodi: Why?...
– Andrew Tomazos
17 hours ago
Because in your sample code, you just call f one hundred times and you have not changed F. So, go for the last one.
– Hoodi
16 hours ago
6
@Hoodi - You have no way to know iff
has any state that changes by callingf()
. That template accepts general functors.
– StoryTeller
16 hours ago
add a comment |
Suppose I want to write a function that calls a nullary function 100 times. Which of these implementations is best and why?
template<typename F>
void call100(F f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(F& f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(const F& f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; i++)
f();
}
Or is there a better implementation?
Update regarding 4
struct S {
S() {}
S(const S&) = delete;
void operator()() const {}
};
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; i++)
f();
}
int main() {
const S s;
call100(s);
}
c++ c++17
Suppose I want to write a function that calls a nullary function 100 times. Which of these implementations is best and why?
template<typename F>
void call100(F f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(F& f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(const F& f) {
for (int i = 0; i < 100; i++)
f();
}
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; i++)
f();
}
Or is there a better implementation?
Update regarding 4
struct S {
S() {}
S(const S&) = delete;
void operator()() const {}
};
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; i++)
f();
}
int main() {
const S s;
call100(s);
}
c++ c++17
c++ c++17
edited 16 hours ago
Andrew Tomazos
asked 17 hours ago
Andrew TomazosAndrew Tomazos
35.9k26134235
35.9k26134235
1
go for the last one.
– Hoodi
17 hours ago
5
@Hoodi: Why?...
– Andrew Tomazos
17 hours ago
Because in your sample code, you just call f one hundred times and you have not changed F. So, go for the last one.
– Hoodi
16 hours ago
6
@Hoodi - You have no way to know iff
has any state that changes by callingf()
. That template accepts general functors.
– StoryTeller
16 hours ago
add a comment |
1
go for the last one.
– Hoodi
17 hours ago
5
@Hoodi: Why?...
– Andrew Tomazos
17 hours ago
Because in your sample code, you just call f one hundred times and you have not changed F. So, go for the last one.
– Hoodi
16 hours ago
6
@Hoodi - You have no way to know iff
has any state that changes by callingf()
. That template accepts general functors.
– StoryTeller
16 hours ago
1
1
go for the last one.
– Hoodi
17 hours ago
go for the last one.
– Hoodi
17 hours ago
5
5
@Hoodi: Why?...
– Andrew Tomazos
17 hours ago
@Hoodi: Why?...
– Andrew Tomazos
17 hours ago
Because in your sample code, you just call f one hundred times and you have not changed F. So, go for the last one.
– Hoodi
16 hours ago
Because in your sample code, you just call f one hundred times and you have not changed F. So, go for the last one.
– Hoodi
16 hours ago
6
6
@Hoodi - You have no way to know if
f
has any state that changes by calling f()
. That template accepts general functors.– StoryTeller
16 hours ago
@Hoodi - You have no way to know if
f
has any state that changes by calling f()
. That template accepts general functors.– StoryTeller
16 hours ago
add a comment |
3 Answers
3
active
oldest
votes
I would use the first one (pass the callable by value).
If a caller is concerned about the cost of copying the callable, then they can use std::ref(f)
or std::cref(f)
to pass it using reference_wrapper
.
By doing this, you provide the most flexibility to the caller.
But in the main code of the question, it JUST calls the f 100 times. No?
– Hoodi
16 hours ago
4
Also note this is how the standard library does it in the algorithmns library (e.g.std::for_each
,std::count_if
)
– Artyer
16 hours ago
4
@Artyer Although those predate forwarding references.
– Barry
11 hours ago
add a comment |
The only runtime cost of
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; ++i)
f();
}
is that it can have more versions (copies of code) if you pass f
in multiple ways. With MSVC or the gold linker with ICF, those copies only cost compile time unless they differ, and if they differ you probably want to keep them.
template<typename F>
void call100(F f) {
for (int i = 0; i < 100; ++i)
f();
}
this one has the advantage of being value semantics; and following the rule of taking values unless you have good reason not to is reasonable. std::ref
/std::cref
let you call it with a persistant reference, and for prvalues c++17 guaranteed elision will prevent a spurious copy.
As a joke you could do:
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 99; ++i)
f();
std::forward<F>(f)();
}
but that relies on people having &&
overloads on their operator()
, which nobody does.
add a comment |
I do not think there is a definitive answer:
The first one copies everything you pass in which might be expensive
for capturing lambdas but otherwise provides the most flexibility:
Pros
- Const objects allowed
- Mutable objects allowed (copied)
- Copy can be elided (?)
Cons
- Copies everything you give it
- You cannot call it with an existing object such as mutable lambda without copying it in
The second one cannot be used for const objects. On the other hand
it does not copy anything and allows mutable objects:
Pros
- Mutable objects allowed
- Copies nothing
Cons
- Does not allow const objects
The third one cannot be used for mutable lambdas so is a slight
modification of the second one.
Pros
- Const objects allowed
- Copies nothing
Cons
- Cannot be called with mutable objects
The fourth one cannot be called with const objects unless you copy
them which becomes quite awkward with lambdas. You also cannot use
it with pre-existing mutable lambda object without copying it or
moving from it (losing it in the process) which is similar
limitation to 1.
Pros
- Avoids copies explicitely by forcing (requiring) move semanthics if the copy is needed
- Mutable objects allowed.
- Const objects allowed (except for mutable lambdas)
Cons
- Does not allow const mutable lambdas without a copy
- You cannot call it with an existing object such as mutable lambda
And there you have it. There is no silver bullet here and there are different pros & cons to each of these versions. I tend to lean towards the first one being the default but with certain types of capturing lambdas or bigger callables, it might become an issue. And you cannot call the 1) with the mutable object and get an expected result. As mentioned in the other answer some of these can be overcome with std::ref
and other ways of manipulating the actual T
type. In my experience, these tend to be the source of pretty nasty bugs though when T
is then something different than one expects to achieve i.e. mutability of a copy or such.
I don't think your analysis of 4 is correct. Passed a const value won't F be deduced as const T& and be passed by reference?
– Andrew Tomazos
16 hours ago
See "Update regarding 4"
– Andrew Tomazos
16 hours ago
@AndrewTomazos Not with const mutable lambdas. Or rather it will deduce it as you say but would refuse to compile because it would discard theconst
at the call site. Using latest MSVC2017, not sure about Clang/GCC as Godbolt seems not to work atm.
– Resurrection
16 hours ago
1
@artyr No, that is nonsense. Feel free to test it yourself, but 4 won't calloperator()&&
.
– Yakk - Adam Nevraumont
16 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55674830%2fpassing-functions-in-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
I would use the first one (pass the callable by value).
If a caller is concerned about the cost of copying the callable, then they can use std::ref(f)
or std::cref(f)
to pass it using reference_wrapper
.
By doing this, you provide the most flexibility to the caller.
But in the main code of the question, it JUST calls the f 100 times. No?
– Hoodi
16 hours ago
4
Also note this is how the standard library does it in the algorithmns library (e.g.std::for_each
,std::count_if
)
– Artyer
16 hours ago
4
@Artyer Although those predate forwarding references.
– Barry
11 hours ago
add a comment |
I would use the first one (pass the callable by value).
If a caller is concerned about the cost of copying the callable, then they can use std::ref(f)
or std::cref(f)
to pass it using reference_wrapper
.
By doing this, you provide the most flexibility to the caller.
But in the main code of the question, it JUST calls the f 100 times. No?
– Hoodi
16 hours ago
4
Also note this is how the standard library does it in the algorithmns library (e.g.std::for_each
,std::count_if
)
– Artyer
16 hours ago
4
@Artyer Although those predate forwarding references.
– Barry
11 hours ago
add a comment |
I would use the first one (pass the callable by value).
If a caller is concerned about the cost of copying the callable, then they can use std::ref(f)
or std::cref(f)
to pass it using reference_wrapper
.
By doing this, you provide the most flexibility to the caller.
I would use the first one (pass the callable by value).
If a caller is concerned about the cost of copying the callable, then they can use std::ref(f)
or std::cref(f)
to pass it using reference_wrapper
.
By doing this, you provide the most flexibility to the caller.
edited 1 hour ago
jww
54.4k41238517
54.4k41238517
answered 17 hours ago
Marshall ClowMarshall Clow
7,6831635
7,6831635
But in the main code of the question, it JUST calls the f 100 times. No?
– Hoodi
16 hours ago
4
Also note this is how the standard library does it in the algorithmns library (e.g.std::for_each
,std::count_if
)
– Artyer
16 hours ago
4
@Artyer Although those predate forwarding references.
– Barry
11 hours ago
add a comment |
But in the main code of the question, it JUST calls the f 100 times. No?
– Hoodi
16 hours ago
4
Also note this is how the standard library does it in the algorithmns library (e.g.std::for_each
,std::count_if
)
– Artyer
16 hours ago
4
@Artyer Although those predate forwarding references.
– Barry
11 hours ago
But in the main code of the question, it JUST calls the f 100 times. No?
– Hoodi
16 hours ago
But in the main code of the question, it JUST calls the f 100 times. No?
– Hoodi
16 hours ago
4
4
Also note this is how the standard library does it in the algorithmns library (e.g.
std::for_each
, std::count_if
)– Artyer
16 hours ago
Also note this is how the standard library does it in the algorithmns library (e.g.
std::for_each
, std::count_if
)– Artyer
16 hours ago
4
4
@Artyer Although those predate forwarding references.
– Barry
11 hours ago
@Artyer Although those predate forwarding references.
– Barry
11 hours ago
add a comment |
The only runtime cost of
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; ++i)
f();
}
is that it can have more versions (copies of code) if you pass f
in multiple ways. With MSVC or the gold linker with ICF, those copies only cost compile time unless they differ, and if they differ you probably want to keep them.
template<typename F>
void call100(F f) {
for (int i = 0; i < 100; ++i)
f();
}
this one has the advantage of being value semantics; and following the rule of taking values unless you have good reason not to is reasonable. std::ref
/std::cref
let you call it with a persistant reference, and for prvalues c++17 guaranteed elision will prevent a spurious copy.
As a joke you could do:
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 99; ++i)
f();
std::forward<F>(f)();
}
but that relies on people having &&
overloads on their operator()
, which nobody does.
add a comment |
The only runtime cost of
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; ++i)
f();
}
is that it can have more versions (copies of code) if you pass f
in multiple ways. With MSVC or the gold linker with ICF, those copies only cost compile time unless they differ, and if they differ you probably want to keep them.
template<typename F>
void call100(F f) {
for (int i = 0; i < 100; ++i)
f();
}
this one has the advantage of being value semantics; and following the rule of taking values unless you have good reason not to is reasonable. std::ref
/std::cref
let you call it with a persistant reference, and for prvalues c++17 guaranteed elision will prevent a spurious copy.
As a joke you could do:
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 99; ++i)
f();
std::forward<F>(f)();
}
but that relies on people having &&
overloads on their operator()
, which nobody does.
add a comment |
The only runtime cost of
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; ++i)
f();
}
is that it can have more versions (copies of code) if you pass f
in multiple ways. With MSVC or the gold linker with ICF, those copies only cost compile time unless they differ, and if they differ you probably want to keep them.
template<typename F>
void call100(F f) {
for (int i = 0; i < 100; ++i)
f();
}
this one has the advantage of being value semantics; and following the rule of taking values unless you have good reason not to is reasonable. std::ref
/std::cref
let you call it with a persistant reference, and for prvalues c++17 guaranteed elision will prevent a spurious copy.
As a joke you could do:
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 99; ++i)
f();
std::forward<F>(f)();
}
but that relies on people having &&
overloads on their operator()
, which nobody does.
The only runtime cost of
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 100; ++i)
f();
}
is that it can have more versions (copies of code) if you pass f
in multiple ways. With MSVC or the gold linker with ICF, those copies only cost compile time unless they differ, and if they differ you probably want to keep them.
template<typename F>
void call100(F f) {
for (int i = 0; i < 100; ++i)
f();
}
this one has the advantage of being value semantics; and following the rule of taking values unless you have good reason not to is reasonable. std::ref
/std::cref
let you call it with a persistant reference, and for prvalues c++17 guaranteed elision will prevent a spurious copy.
As a joke you could do:
template<typename F>
void call100(F&& f) {
for (int i = 0; i < 99; ++i)
f();
std::forward<F>(f)();
}
but that relies on people having &&
overloads on their operator()
, which nobody does.
edited 14 hours ago
answered 16 hours ago
Yakk - Adam NevraumontYakk - Adam Nevraumont
189k21200385
189k21200385
add a comment |
add a comment |
I do not think there is a definitive answer:
The first one copies everything you pass in which might be expensive
for capturing lambdas but otherwise provides the most flexibility:
Pros
- Const objects allowed
- Mutable objects allowed (copied)
- Copy can be elided (?)
Cons
- Copies everything you give it
- You cannot call it with an existing object such as mutable lambda without copying it in
The second one cannot be used for const objects. On the other hand
it does not copy anything and allows mutable objects:
Pros
- Mutable objects allowed
- Copies nothing
Cons
- Does not allow const objects
The third one cannot be used for mutable lambdas so is a slight
modification of the second one.
Pros
- Const objects allowed
- Copies nothing
Cons
- Cannot be called with mutable objects
The fourth one cannot be called with const objects unless you copy
them which becomes quite awkward with lambdas. You also cannot use
it with pre-existing mutable lambda object without copying it or
moving from it (losing it in the process) which is similar
limitation to 1.
Pros
- Avoids copies explicitely by forcing (requiring) move semanthics if the copy is needed
- Mutable objects allowed.
- Const objects allowed (except for mutable lambdas)
Cons
- Does not allow const mutable lambdas without a copy
- You cannot call it with an existing object such as mutable lambda
And there you have it. There is no silver bullet here and there are different pros & cons to each of these versions. I tend to lean towards the first one being the default but with certain types of capturing lambdas or bigger callables, it might become an issue. And you cannot call the 1) with the mutable object and get an expected result. As mentioned in the other answer some of these can be overcome with std::ref
and other ways of manipulating the actual T
type. In my experience, these tend to be the source of pretty nasty bugs though when T
is then something different than one expects to achieve i.e. mutability of a copy or such.
I don't think your analysis of 4 is correct. Passed a const value won't F be deduced as const T& and be passed by reference?
– Andrew Tomazos
16 hours ago
See "Update regarding 4"
– Andrew Tomazos
16 hours ago
@AndrewTomazos Not with const mutable lambdas. Or rather it will deduce it as you say but would refuse to compile because it would discard theconst
at the call site. Using latest MSVC2017, not sure about Clang/GCC as Godbolt seems not to work atm.
– Resurrection
16 hours ago
1
@artyr No, that is nonsense. Feel free to test it yourself, but 4 won't calloperator()&&
.
– Yakk - Adam Nevraumont
16 hours ago
add a comment |
I do not think there is a definitive answer:
The first one copies everything you pass in which might be expensive
for capturing lambdas but otherwise provides the most flexibility:
Pros
- Const objects allowed
- Mutable objects allowed (copied)
- Copy can be elided (?)
Cons
- Copies everything you give it
- You cannot call it with an existing object such as mutable lambda without copying it in
The second one cannot be used for const objects. On the other hand
it does not copy anything and allows mutable objects:
Pros
- Mutable objects allowed
- Copies nothing
Cons
- Does not allow const objects
The third one cannot be used for mutable lambdas so is a slight
modification of the second one.
Pros
- Const objects allowed
- Copies nothing
Cons
- Cannot be called with mutable objects
The fourth one cannot be called with const objects unless you copy
them which becomes quite awkward with lambdas. You also cannot use
it with pre-existing mutable lambda object without copying it or
moving from it (losing it in the process) which is similar
limitation to 1.
Pros
- Avoids copies explicitely by forcing (requiring) move semanthics if the copy is needed
- Mutable objects allowed.
- Const objects allowed (except for mutable lambdas)
Cons
- Does not allow const mutable lambdas without a copy
- You cannot call it with an existing object such as mutable lambda
And there you have it. There is no silver bullet here and there are different pros & cons to each of these versions. I tend to lean towards the first one being the default but with certain types of capturing lambdas or bigger callables, it might become an issue. And you cannot call the 1) with the mutable object and get an expected result. As mentioned in the other answer some of these can be overcome with std::ref
and other ways of manipulating the actual T
type. In my experience, these tend to be the source of pretty nasty bugs though when T
is then something different than one expects to achieve i.e. mutability of a copy or such.
I don't think your analysis of 4 is correct. Passed a const value won't F be deduced as const T& and be passed by reference?
– Andrew Tomazos
16 hours ago
See "Update regarding 4"
– Andrew Tomazos
16 hours ago
@AndrewTomazos Not with const mutable lambdas. Or rather it will deduce it as you say but would refuse to compile because it would discard theconst
at the call site. Using latest MSVC2017, not sure about Clang/GCC as Godbolt seems not to work atm.
– Resurrection
16 hours ago
1
@artyr No, that is nonsense. Feel free to test it yourself, but 4 won't calloperator()&&
.
– Yakk - Adam Nevraumont
16 hours ago
add a comment |
I do not think there is a definitive answer:
The first one copies everything you pass in which might be expensive
for capturing lambdas but otherwise provides the most flexibility:
Pros
- Const objects allowed
- Mutable objects allowed (copied)
- Copy can be elided (?)
Cons
- Copies everything you give it
- You cannot call it with an existing object such as mutable lambda without copying it in
The second one cannot be used for const objects. On the other hand
it does not copy anything and allows mutable objects:
Pros
- Mutable objects allowed
- Copies nothing
Cons
- Does not allow const objects
The third one cannot be used for mutable lambdas so is a slight
modification of the second one.
Pros
- Const objects allowed
- Copies nothing
Cons
- Cannot be called with mutable objects
The fourth one cannot be called with const objects unless you copy
them which becomes quite awkward with lambdas. You also cannot use
it with pre-existing mutable lambda object without copying it or
moving from it (losing it in the process) which is similar
limitation to 1.
Pros
- Avoids copies explicitely by forcing (requiring) move semanthics if the copy is needed
- Mutable objects allowed.
- Const objects allowed (except for mutable lambdas)
Cons
- Does not allow const mutable lambdas without a copy
- You cannot call it with an existing object such as mutable lambda
And there you have it. There is no silver bullet here and there are different pros & cons to each of these versions. I tend to lean towards the first one being the default but with certain types of capturing lambdas or bigger callables, it might become an issue. And you cannot call the 1) with the mutable object and get an expected result. As mentioned in the other answer some of these can be overcome with std::ref
and other ways of manipulating the actual T
type. In my experience, these tend to be the source of pretty nasty bugs though when T
is then something different than one expects to achieve i.e. mutability of a copy or such.
I do not think there is a definitive answer:
The first one copies everything you pass in which might be expensive
for capturing lambdas but otherwise provides the most flexibility:
Pros
- Const objects allowed
- Mutable objects allowed (copied)
- Copy can be elided (?)
Cons
- Copies everything you give it
- You cannot call it with an existing object such as mutable lambda without copying it in
The second one cannot be used for const objects. On the other hand
it does not copy anything and allows mutable objects:
Pros
- Mutable objects allowed
- Copies nothing
Cons
- Does not allow const objects
The third one cannot be used for mutable lambdas so is a slight
modification of the second one.
Pros
- Const objects allowed
- Copies nothing
Cons
- Cannot be called with mutable objects
The fourth one cannot be called with const objects unless you copy
them which becomes quite awkward with lambdas. You also cannot use
it with pre-existing mutable lambda object without copying it or
moving from it (losing it in the process) which is similar
limitation to 1.
Pros
- Avoids copies explicitely by forcing (requiring) move semanthics if the copy is needed
- Mutable objects allowed.
- Const objects allowed (except for mutable lambdas)
Cons
- Does not allow const mutable lambdas without a copy
- You cannot call it with an existing object such as mutable lambda
And there you have it. There is no silver bullet here and there are different pros & cons to each of these versions. I tend to lean towards the first one being the default but with certain types of capturing lambdas or bigger callables, it might become an issue. And you cannot call the 1) with the mutable object and get an expected result. As mentioned in the other answer some of these can be overcome with std::ref
and other ways of manipulating the actual T
type. In my experience, these tend to be the source of pretty nasty bugs though when T
is then something different than one expects to achieve i.e. mutability of a copy or such.
edited 13 hours ago
JeJo
4,5173926
4,5173926
answered 16 hours ago
ResurrectionResurrection
2,05911839
2,05911839
I don't think your analysis of 4 is correct. Passed a const value won't F be deduced as const T& and be passed by reference?
– Andrew Tomazos
16 hours ago
See "Update regarding 4"
– Andrew Tomazos
16 hours ago
@AndrewTomazos Not with const mutable lambdas. Or rather it will deduce it as you say but would refuse to compile because it would discard theconst
at the call site. Using latest MSVC2017, not sure about Clang/GCC as Godbolt seems not to work atm.
– Resurrection
16 hours ago
1
@artyr No, that is nonsense. Feel free to test it yourself, but 4 won't calloperator()&&
.
– Yakk - Adam Nevraumont
16 hours ago
add a comment |
I don't think your analysis of 4 is correct. Passed a const value won't F be deduced as const T& and be passed by reference?
– Andrew Tomazos
16 hours ago
See "Update regarding 4"
– Andrew Tomazos
16 hours ago
@AndrewTomazos Not with const mutable lambdas. Or rather it will deduce it as you say but would refuse to compile because it would discard theconst
at the call site. Using latest MSVC2017, not sure about Clang/GCC as Godbolt seems not to work atm.
– Resurrection
16 hours ago
1
@artyr No, that is nonsense. Feel free to test it yourself, but 4 won't calloperator()&&
.
– Yakk - Adam Nevraumont
16 hours ago
I don't think your analysis of 4 is correct. Passed a const value won't F be deduced as const T& and be passed by reference?
– Andrew Tomazos
16 hours ago
I don't think your analysis of 4 is correct. Passed a const value won't F be deduced as const T& and be passed by reference?
– Andrew Tomazos
16 hours ago
See "Update regarding 4"
– Andrew Tomazos
16 hours ago
See "Update regarding 4"
– Andrew Tomazos
16 hours ago
@AndrewTomazos Not with const mutable lambdas. Or rather it will deduce it as you say but would refuse to compile because it would discard the
const
at the call site. Using latest MSVC2017, not sure about Clang/GCC as Godbolt seems not to work atm.– Resurrection
16 hours ago
@AndrewTomazos Not with const mutable lambdas. Or rather it will deduce it as you say but would refuse to compile because it would discard the
const
at the call site. Using latest MSVC2017, not sure about Clang/GCC as Godbolt seems not to work atm.– Resurrection
16 hours ago
1
1
@artyr No, that is nonsense. Feel free to test it yourself, but 4 won't call
operator()&&
.– Yakk - Adam Nevraumont
16 hours ago
@artyr No, that is nonsense. Feel free to test it yourself, but 4 won't call
operator()&&
.– Yakk - Adam Nevraumont
16 hours ago
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55674830%2fpassing-functions-in-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
go for the last one.
– Hoodi
17 hours ago
5
@Hoodi: Why?...
– Andrew Tomazos
17 hours ago
Because in your sample code, you just call f one hundred times and you have not changed F. So, go for the last one.
– Hoodi
16 hours ago
6
@Hoodi - You have no way to know if
f
has any state that changes by callingf()
. That template accepts general functors.– StoryTeller
16 hours ago