64,643
社区成员
发帖
与我相关
我的任务
分享
template <typename TContaienr, typename TKey, typename TValue>
const TValue& FindWithDefault(const TContaienr& c, const TKey& k, const TValue& v) {
const typename TContaienr::const_iterator iter = c.find(k);
if (iter == c.end()) {
return iter->second;
} else {
return v;
}
}
template<typename TKey>
float FindRateWithDefault(const boost::unordered_map<TKey, float>& rates,
const TKey& key,
const float default_value) {
const typename boost::unordered_map<TKey, float>::const_iterator iter =
rates.find(key);
if (iter != rates.end()) {
return iter->second;
} else {
return default_value;
}
}
[/quote]
过奖了。
下面是我新改的,也修正了 #8 指出的问题。
先说一下逻辑,这样看起来更自然。
假设容器元素类型是 T,即 set<T> or map<...,T>,调用端通过 const reference 接收,且复制 T 需要尽量避免。
对于 FindWithDefault 那样的需求,需要确定返回值类型。
(1) 如果能够找到,或没找到但默认值是左值,则应该返回 const T&,避免复制。
(2) 如果没找到并且默认值是右值,则必须返回默认值的副本,避免出现 dangling reference.
问题是能够找到是运行时的信息,而返回类型必须编译期确定。
#7 的那个通过自己管理 raw storage,实现指针 (const T&) 和值 (T) 之间的按需变换。
这种方法有一些缺点:
[1] 需要自己写实现,麻烦也容易出现疏漏。
[2] 按需变换仍然要使用 sizeof(T) and sizeof(T*) 两者中大的对象类型对应的存储空间,唯一节省的就是 T 的构造。
返回来看原来确定返回类型的逻辑,唯一的不和谐就在于,missing with rvalue default 的时候,默认值会在 full expression 结束时消失。如果能够想办法让它不消失,则返回类型能够确定为 const T& 或其类似的东西。对于这种情况,可以使用 new,通过堆空间过渡。这又带来了内存管理的问题,最省事儿的莫过于 std::unique_ptr or std::shared_ptr 了,唯一需要提供的就是一个自定义的 deleter,能够识别管理的对象是否需要由自身删除,这样 missing with rvalue default 的时候执行删除,其他的时候啥都不干就好了,因此就有了下面的代码。
不过这种方法也有问题:
(a) 经常赶上 missing with rvalue default 的情况时,容易带来过多的小对象分配。
(b) 对于 trivially copyable T 类型,像 #16 给的那个 float 的用例,通过 new 完全没有必要。所有情况下都返回复制是最佳策略。
(b) 的解决方案比较容易,可以通过 is_trivially_copyable<T> 来分开不同的逻辑。不过这会带来调用端语法不统一的缺陷,i.e., pointer vs object access syntax.
(a) 的就比较麻烦了。最好的貌似也就是把 FindWithDefault 设计成类,然后通过数据成员使用内存池降低消耗。
慢慢的这个问题越变越复杂,已经有过度设计的影子了。
写了这么多,我觉得最好的方案就是库函数和调用端配合,对于 trivially copyable 类型,直接返回副本。对于复制起来很贵的类型,强制调用端提供左值的默认值 (through function = delete construct),然后统一返回 const T&。
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <unordered_set>
#include <unordered_map>
namespace
{
namespace find_with_default_helper_t
{
template <typename> struct get_value_t;
template <typename K, typename C, typename A>
struct get_value_t<std::set<K,C,A>>
{
static K const& f (typename std::set<K,C,A>::const_iterator const& x)
{
return *x;
}
};
template <typename K, typename H, typename E, typename A>
struct get_value_t<std::unordered_set<K,H,E,A>>
{
static K const& f (typename std::unordered_set<K,H,E,A>::const_iterator const& x)
{
return *x;
}
};
template <typename K, typename T, typename C, typename A>
struct get_value_t<std::map<K,T,C,A>>
{
static T const& f (typename std::map<K,T,C,A>::const_iterator const& x)
{
return x->second;
}
};
template <typename K, typename T, typename H, typename E, typename A>
struct get_value_t<std::unordered_map<K,T,H,E,A>>
{
static T const& f (typename std::unordered_map<K,T,H,E,A>::const_iterator const& x)
{
return x->second;
}
};
template <typename C>
using element_t = typename std::decay<decltype(get_value_t<C>::f(std::declval<C>().begin()))>::type;
template <typename C>
element_t<C>const* conditional_copy (element_t<C>const& t) { return &t; }
template <typename C>
element_t<C>const* conditional_copy (element_t<C>&& t) { return new (element_t<C>) (std::move(t)); }
template <typename T>
struct conditional_deleter_t
{
conditional_deleter_t (bool x = false) : is_deletable(x) { }
bool is_deletable;
void operator () (T* ptr) const
{
if (is_deletable)
{
delete ptr;
}
}
};
template <typename T> using pointer_t = std::unique_ptr<T,conditional_deleter_t<T>>;
// template <typename T> using pointer_t = std::shared_ptr<T>;
}
}
template <typename C, typename K, typename T>
find_with_default_helper_t::pointer_t<find_with_default_helper_t::element_t<C>const>
FindWithDefault (const C& c, const K& k, T&& v)
{
using element_t = find_with_default_helper_t::element_t<C>const;
using pointer_t = find_with_default_helper_t::pointer_t<element_t>;
using conditional_deleter_t = find_with_default_helper_t::conditional_deleter_t<element_t>;
auto const it = c.find(k);
if (it != c.end())
{
return pointer_t(&find_with_default_helper_t::get_value_t<C>::f(it),
conditional_deleter_t{false});
}
else
{
return pointer_t(find_with_default_helper_t::conditional_copy<C>(std::forward<T>(v)),
conditional_deleter_t{std::is_rvalue_reference<T&&>::value});
}
}
struct marker_t
{
~marker_t ()
{
if (verbose)
{
std::cout << "@" << this << " dtor " << string << std::endl;
}
}
marker_t (std::string const& s, bool const b = false) : string(s), verbose(b)
{
if (verbose)
{
std::cout << "@" << this << " ctor " << string << std::endl;
}
}
marker_t (marker_t const& x) : string(x.string), verbose(x.verbose)
{
if (verbose)
{
std::cout << "@" << this << " copy ctor " << string << std::endl;
}
}
marker_t (marker_t&& x) : string(std::move(x.string)), verbose(std::move(x.verbose))
{
if (verbose)
{
std::cout << "@" << this << " move ctor " << string << std::endl;
}
}
std::string string;
bool verbose = false;
};
int main ()
{
std::unordered_map<size_t,marker_t>const container
{
{1,{"test",true}},
};
std::cout << std::endl;
{
marker_t const solid{"found && default-solid",true};
auto const& ans = FindWithDefault(container,1,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const& ans = FindWithDefault(container,1,marker_t{"found && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
marker_t const solid{"missing && default-solid",true};
auto const& ans = FindWithDefault(container,0,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const& ans = FindWithDefault(container,0,marker_t{"missing && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
// auto const aa = ans;
// std::cout << "printing: " << aa->string << std::endl;
}
std::cout << std::endl;
}
#include <functional>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <utility>
template <typename T>
struct optional_default_t
{
~optional_default_t () { destruct(); }
optional_default_t (T&& t) : is_object(true) { construct(std::move(t)); }
optional_default_t (T const& t) : optional_default_t(&t) { }
optional_default_t (T const* p) : is_object(false) { construct(p); }
optional_default_t (optional_default_t const& x) : is_object(x.is_object)
{
construct(x);
}
optional_default_t (optional_default_t&& x) : is_object(std::move(x.is_object))
{
construct(std::move(x));
}
optional_default_t& operator = (optional_default_t const& x)
{
if (this != x)
{
destruct();
is_object = x.is_object;
construct(x);
}
}
optional_default_t& operator = (optional_default_t&& x)
{
if (this != &x)
{
destruct();
is_object = std::move(x.is_object);
construct(std::move(x));
}
}
T const& get () const
{
return is_object?
reinterpret_cast<T const&>(storage):
reinterpret_cast<T const*const*>(&storage)[0][0];
}
operator T const& () const { return get(); }
T const* operator->() const { return &get(); }
T const* operator->() { return &get(); }
private:
using U = typename std::conditional<sizeof(T)<sizeof(T*),T*,T>::type;
using S = typename std::aligned_storage<sizeof(U),std::alignment_of<U>::value>::type;
bool is_object;
S storage;
void * address () { return &storage; }
void const* address () const { return &storage; }
void destruct () { if (is_object) { get().T::~T(); } }
template <typename U>
void construct (U&& u)
{
new (address()) (typename std::remove_reference<U>::type) (std::forward<U>(u));
}
void construct (optional_default_t const& x)
{
x.is_object? construct( x.get()):
construct(&x.get());
}
void construct (optional_default_t&& x)
{
x.is_object? construct(std::move( x.get())):
construct(std::move(&x.get()));
}
};
template <typename T>
T const& get_value (T const& x)
{
return x;
}
template <typename T, typename U>
U const& get_value (std::pair<T,U>const& x)
{
return x.second;
}
template <typename C, typename K, typename T>
optional_default_t<typename std::decay<decltype(get_value(*std::declval<C>().begin()))>::type>
FindWithDefault(const C& c, const K& k, T&& v)
{
auto const it = c.find(k);
if (it != c.end())
{
return &get_value(*it);
}
else
{
return std::forward<T>(v);
}
}
struct marker_t
{
~marker_t ()
{
if (verbose)
{
std::cout << "@" << this << " dtor " << string << std::endl;
}
}
marker_t (std::string const& s, bool const b = false) : string(s), verbose(b)
{
if (verbose)
{
std::cout << "@" << this << " ctor " << string << std::endl;
}
}
marker_t (marker_t const& x) : string(x.string), verbose(x.verbose)
{
if (verbose)
{
std::cout << "@" << this << " copy ctor " << string << std::endl;
}
}
marker_t (marker_t&& x) : string(std::move(x.string)), verbose(std::move(x.verbose))
{
if (verbose)
{
std::cout << "@" << this << " move ctor " << string << std::endl;
}
}
std::string string;
bool verbose = false;
};
int main ()
{
std::map<size_t,marker_t>const container
{
{1,{"test",true}},
};
std::cout << std::endl;
{
marker_t const solid{"found && default-solid",true};
auto const ans = FindWithDefault(container,1,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const ans = FindWithDefault(container,1,marker_t{"found && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
marker_t const solid{"missing && default-solid",true};
auto const ans = FindWithDefault(container,0,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const ans = FindWithDefault(container,0,marker_t{"missing && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
}
[/quote]
非常感谢,有没有完美的答案并不重要。
重要的是经过有心人的讨论研究,得出在现行条件下我们能解决到什么程度的结论。
这个工作还是很值得敬佩的,尤其我这边显示的回帖时间是北京时间凌晨三点多,不知道阁下的地方时是?[/quote]
我这里是下午,和北京刚好差 12 小时。
别误会,觉还是要睡的,
和别人交流也促使自己学习,这里经常能看到别人的精彩回复。
比如这个帖子让我认识到,提供默认值的难度(虽然这事儿看起来好像很没难度),从而也在一定程度上理解了标准为什么没有支持类似的功能。说到着,我记得前一阵看到过讨论,下一般标准好像要加类似于 boost::optional 的东西。[/quote]
原来是来自那半球的声音。
#include <functional>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <utility>
template <typename T>
struct optional_default_t
{
~optional_default_t () { destruct(); }
optional_default_t (T&& t) : is_object(true) { construct(std::move(t)); }
optional_default_t (T const& t) : optional_default_t(&t) { }
optional_default_t (T const* p) : is_object(false) { construct(p); }
optional_default_t (optional_default_t const& x) : is_object(x.is_object)
{
construct(x);
}
optional_default_t (optional_default_t&& x) : is_object(std::move(x.is_object))
{
construct(std::move(x));
}
optional_default_t& operator = (optional_default_t const& x)
{
if (this != x)
{
destruct();
is_object = x.is_object;
construct(x);
}
}
optional_default_t& operator = (optional_default_t&& x)
{
if (this != &x)
{
destruct();
is_object = std::move(x.is_object);
construct(std::move(x));
}
}
T const& get () const
{
return is_object?
reinterpret_cast<T const&>(storage):
reinterpret_cast<T const*const*>(&storage)[0][0];
}
operator T const& () const { return get(); }
T const* operator->() const { return &get(); }
T const* operator->() { return &get(); }
private:
using U = typename std::conditional<sizeof(T)<sizeof(T*),T*,T>::type;
using S = typename std::aligned_storage<sizeof(U),std::alignment_of<U>::value>::type;
bool is_object;
S storage;
void * address () { return &storage; }
void const* address () const { return &storage; }
void destruct () { if (is_object) { get().T::~T(); } }
template <typename U>
void construct (U&& u)
{
new (address()) (typename std::remove_reference<U>::type) (std::forward<U>(u));
}
void construct (optional_default_t const& x)
{
x.is_object? construct( x.get()):
construct(&x.get());
}
void construct (optional_default_t&& x)
{
x.is_object? construct(std::move( x.get())):
construct(std::move(&x.get()));
}
};
template <typename T>
T const& get_value (T const& x)
{
return x;
}
template <typename T, typename U>
U const& get_value (std::pair<T,U>const& x)
{
return x.second;
}
template <typename C, typename K, typename T>
optional_default_t<typename std::decay<decltype(get_value(*std::declval<C>().begin()))>::type>
FindWithDefault(const C& c, const K& k, T&& v)
{
auto const it = c.find(k);
if (it != c.end())
{
return &get_value(*it);
}
else
{
return std::forward<T>(v);
}
}
struct marker_t
{
~marker_t ()
{
if (verbose)
{
std::cout << "@" << this << " dtor " << string << std::endl;
}
}
marker_t (std::string const& s, bool const b = false) : string(s), verbose(b)
{
if (verbose)
{
std::cout << "@" << this << " ctor " << string << std::endl;
}
}
marker_t (marker_t const& x) : string(x.string), verbose(x.verbose)
{
if (verbose)
{
std::cout << "@" << this << " copy ctor " << string << std::endl;
}
}
marker_t (marker_t&& x) : string(std::move(x.string)), verbose(std::move(x.verbose))
{
if (verbose)
{
std::cout << "@" << this << " move ctor " << string << std::endl;
}
}
std::string string;
bool verbose = false;
};
int main ()
{
std::map<size_t,marker_t>const container
{
{1,{"test",true}},
};
std::cout << std::endl;
{
marker_t const solid{"found && default-solid",true};
auto const ans = FindWithDefault(container,1,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const ans = FindWithDefault(container,1,marker_t{"found && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
marker_t const solid{"missing && default-solid",true};
auto const ans = FindWithDefault(container,0,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const ans = FindWithDefault(container,0,marker_t{"missing && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
}
[/quote]
#include <functional>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <utility>
template <typename T>
struct optional_default_t
{
~optional_default_t () { destruct(); }
optional_default_t (T&& t) : is_object(true) { construct(std::move(t)); }
optional_default_t (T const& t) : optional_default_t(&t) { }
optional_default_t (T const* p) : is_object(false) { construct(p); }
optional_default_t (optional_default_t const& x) : is_object(x.is_object)
{
construct(x);
}
optional_default_t (optional_default_t&& x) : is_object(std::move(x.is_object))
{
construct(std::move(x));
}
optional_default_t& operator = (optional_default_t const& x)
{
if (this != x)
{
destruct();
is_object = x.is_object;
construct(x);
}
}
optional_default_t& operator = (optional_default_t&& x)
{
if (this != &x)
{
destruct();
is_object = std::move(x.is_object);
construct(std::move(x));
}
}
T const& get () const
{
return is_object?
reinterpret_cast<T const&>(storage):
reinterpret_cast<T const*const*>(&storage)[0][0];
}
operator T const& () const { return get(); }
T const* operator->() const { return &get(); }
T const* operator->() { return &get(); }
private:
using U = typename std::conditional<sizeof(T)<sizeof(T*),T*,T>::type;
using S = typename std::aligned_storage<sizeof(U),std::alignment_of<U>::value>::type;
bool is_object;
S storage;
void * address () { return &storage; }
void const* address () const { return &storage; }
void destruct () { if (is_object) { get().T::~T(); } }
template <typename U>
void construct (U&& u)
{
new (address()) (typename std::remove_reference<U>::type) (std::forward<U>(u));
}
void construct (optional_default_t const& x)
{
x.is_object? construct( x.get()):
construct(&x.get());
}
void construct (optional_default_t&& x)
{
x.is_object? construct(std::move( x.get())):
construct(std::move(&x.get()));
}
};
template <typename T>
T const& get_value (T const& x)
{
return x;
}
template <typename T, typename U>
U const& get_value (std::pair<T,U>const& x)
{
return x.second;
}
template <typename C, typename K, typename T>
optional_default_t<typename std::decay<decltype(get_value(*std::declval<C>().begin()))>::type>
FindWithDefault(const C& c, const K& k, T&& v)
{
auto const it = c.find(k);
if (it != c.end())
{
return &get_value(*it);
}
else
{
return std::forward<T>(v);
}
}
struct marker_t
{
~marker_t ()
{
if (verbose)
{
std::cout << "@" << this << " dtor " << string << std::endl;
}
}
marker_t (std::string const& s, bool const b = false) : string(s), verbose(b)
{
if (verbose)
{
std::cout << "@" << this << " ctor " << string << std::endl;
}
}
marker_t (marker_t const& x) : string(x.string), verbose(x.verbose)
{
if (verbose)
{
std::cout << "@" << this << " copy ctor " << string << std::endl;
}
}
marker_t (marker_t&& x) : string(std::move(x.string)), verbose(std::move(x.verbose))
{
if (verbose)
{
std::cout << "@" << this << " move ctor " << string << std::endl;
}
}
std::string string;
bool verbose = false;
};
int main ()
{
std::map<size_t,marker_t>const container
{
{1,{"test",true}},
};
std::cout << std::endl;
{
marker_t const solid{"found && default-solid",true};
auto const ans = FindWithDefault(container,1,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const ans = FindWithDefault(container,1,marker_t{"found && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
marker_t const solid{"missing && default-solid",true};
auto const ans = FindWithDefault(container,0,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const ans = FindWithDefault(container,0,marker_t{"missing && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
}
[/quote]
非常感谢,有没有完美的答案并不重要。
重要的是经过有心人的讨论研究,得出在现行条件下我们能解决到什么程度的结论。
这个工作还是很值得敬佩的,尤其我这边显示的回帖时间是北京时间凌晨三点多,不知道阁下的地方时是?
#include <functional>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <utility>
template <typename T>
struct optional_default_t
{
~optional_default_t () { destruct(); }
optional_default_t (T&& t) : is_object(true) { construct(std::move(t)); }
optional_default_t (T const& t) : optional_default_t(&t) { }
optional_default_t (T const* p) : is_object(false) { construct(p); }
optional_default_t (optional_default_t const& x) : is_object(x.is_object)
{
construct(x);
}
optional_default_t (optional_default_t&& x) : is_object(std::move(x.is_object))
{
construct(std::move(x));
}
optional_default_t& operator = (optional_default_t const& x)
{
if (this != x)
{
destruct();
is_object = x.is_object;
construct(x);
}
}
optional_default_t& operator = (optional_default_t&& x)
{
if (this != &x)
{
destruct();
is_object = std::move(x.is_object);
construct(std::move(x));
}
}
T const& get () const
{
return is_object?
reinterpret_cast<T const&>(storage):
reinterpret_cast<T const*const*>(&storage)[0][0];
}
operator T const& () const { return get(); }
T const* operator->() const { return &get(); }
T const* operator->() { return &get(); }
private:
using U = typename std::conditional<sizeof(T)<sizeof(T*),T*,T>::type;
using S = typename std::aligned_storage<sizeof(U),std::alignment_of<U>::value>::type;
bool is_object;
S storage;
void * address () { return &storage; }
void const* address () const { return &storage; }
void destruct () { if (is_object) { get().T::~T(); } }
template <typename U>
void construct (U&& u)
{
new (address()) (typename std::remove_reference<U>::type) (std::forward<U>(u));
}
void construct (optional_default_t const& x)
{
x.is_object? construct( x.get()):
construct(&x.get());
}
void construct (optional_default_t&& x)
{
x.is_object? construct(std::move( x.get())):
construct(std::move(&x.get()));
}
};
template <typename T>
T const& get_value (T const& x)
{
return x;
}
template <typename T, typename U>
U const& get_value (std::pair<T,U>const& x)
{
return x.second;
}
template <typename C, typename K, typename T>
optional_default_t<typename std::decay<decltype(get_value(*std::declval<C>().begin()))>::type>
FindWithDefault(const C& c, const K& k, T&& v)
{
auto const it = c.find(k);
if (it != c.end())
{
return &get_value(*it);
}
else
{
return std::forward<T>(v);
}
}
struct marker_t
{
~marker_t ()
{
if (verbose)
{
std::cout << "@" << this << " dtor " << string << std::endl;
}
}
marker_t (std::string const& s, bool const b = false) : string(s), verbose(b)
{
if (verbose)
{
std::cout << "@" << this << " ctor " << string << std::endl;
}
}
marker_t (marker_t const& x) : string(x.string), verbose(x.verbose)
{
if (verbose)
{
std::cout << "@" << this << " copy ctor " << string << std::endl;
}
}
marker_t (marker_t&& x) : string(std::move(x.string)), verbose(std::move(x.verbose))
{
if (verbose)
{
std::cout << "@" << this << " move ctor " << string << std::endl;
}
}
std::string string;
bool verbose = false;
};
int main ()
{
std::map<size_t,marker_t>const container
{
{1,{"test",true}},
};
std::cout << std::endl;
{
marker_t const solid{"found && default-solid",true};
auto const ans = FindWithDefault(container,1,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const ans = FindWithDefault(container,1,marker_t{"found && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
marker_t const solid{"missing && default-solid",true};
auto const ans = FindWithDefault(container,0,solid);
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
{
auto const ans = FindWithDefault(container,0,marker_t{"missing && default-temp",true});
std::cout << "printing: " << ans->string << std::endl;
}
std::cout << std::endl;
}
template<typename TKey>
float FindRateWithDefault(const boost::unordered_map<TKey, float>& rates,
const TKey& key,
const float default_value) {
const typename boost::unordered_map<TKey, float>::const_iterator iter =
rates.find(key);
if (iter != rates.end()) {
return iter->second;
} else {
return default_value;
}
}
#include <map>
#include <iostream>
#include <string>
template <typename T>
T const& get_value (T const& x)
{
return x;
}
template <typename T, typename U>
U const& get_value (std::pair<T,U>const& x)
{
return x.second;
}
template <typename TContainer, typename TKey, typename TValue>
const TValue& FindWithDefault(const TContainer& c, const TKey& k, const TValue& v) {
const typename TContainer::const_iterator iter = c.find(k);
if (iter != c.end()) {
return get_value(*iter);
// return iter->second;
} else {
return v;
}
}
struct test_t
{
~test_t ()
{
if (verbose)
{
std::cout << "@" << this << " dtor " << string << std::endl;
}
}
test_t (std::string const& s, bool const b = false) : string(s), verbose(b)
{
if (verbose)
{
std::cout << "@" << this << " ctor " << string << std::endl;
}
}
std::string string;
bool verbose = false;
};
int main ()
{
std::map<size_t,test_t>const test
{
{1,{"test"}},
{2,{"retest"}},
};
// temporary variable destructed at the end of ;
test_t const& ub_test = FindWithDefault(test,3,test_t{"default",true});
// UB, attempting to access data member of destructed object.
std::cout << "printing: " << ub_test.string << std::endl;
}
在我这里(g++-4.8.1)的输出结果是:
@0x7fff4b8aa1f0 ctor default
@0x7fff4b8aa1f0 dtor default
printing:
可见 printing 之前对象已经析构了。
这里可以用 perfect forward 解决。见下面代码中的两个 ans 例子。
第一个使用临时变量,通过 move 返回默认值;第二个使用本地变量,直接抓取的引用。
#include <map>
#include <iostream>
#include <string>
template <typename T>
T const& get_value (T const& x)
{
return x;
}
template <typename T, typename U>
U const& get_value (std::pair<T,U>const& x)
{
return x.second;
}
template <typename TContainer, typename TKey, typename TValue>
TValue FindWithDefault(const TContainer& c, const TKey& k, TValue&& v) {
const typename TContainer::const_iterator iter = c.find(k);
if (iter != c.end()) {
return get_value(*iter);
// return iter->second;
} else {
return std::forward<TValue>(v);
}
}
struct test_t
{
~test_t ()
{
if (verbose)
{
std::cout << "@" << this << " dtor " << string << std::endl;
}
}
test_t (std::string const& s, bool const b = false) : string(s), verbose(b)
{
if (verbose)
{
std::cout << "@" << this << " ctor " << string << std::endl;
}
}
test_t (test_t const& x) : string(x.string), verbose(x.verbose)
{
if (verbose)
{
std::cout << "@" << this << " copy ctor " << string << std::endl;
}
}
test_t (test_t&& x) : string(std::move(x.string)), verbose(std::move(x.verbose))
{
if (verbose)
{
std::cout << "@" << this << " move ctor " << string << std::endl;
}
}
std::string string;
bool verbose = false;
};
int main ()
{
std::map<size_t,test_t>const map
{
{1,{"map"}},
{2,{"retest"}},
};
{
test_t const& ans = FindWithDefault(map,3,test_t{"temp",true});
std::cout << "printing: " << ans.string << std::endl;
}
std::cout << std::endl;
{
test_t const solid{"solid",true};
test_t const& ans = FindWithDefault(map,3,solid);
std::cout << "printing: " << ans.string << std::endl;
}
}
我这里输出结果:
@0x7fff8fe3ac20 ctor temp
@0x7fff8fe3abe0 move ctor temp
@0x7fff8fe3ac20 dtor
printing: temp
@0x7fff8fe3abe0 dtor temp
@0x7fff8fe3ac20 ctor solid
printing: solid
@0x7fff8fe3ac20 dtor solid