我發佈了我自己的問題的答案,希望澄清boost::type_erasure::any
的預期用途,而不會使原始問題太冗長。
此答案通過將boost::type_erasure::any
隱藏在另一個類的接口後面併爲可轉換爲std::string
的所有類型提供setter方法的超載來顯示可能的解決方法。這個過載需要注意將char指針和char數組轉換爲std::string
。
我認爲一個額外的重載並不壞,但理想情況下,我想避免該樣板,並使any
類型知道如何轉換C字符串。這使我們回到我的original question。
#include <iostream>
#include <unordered_map>
#include <string>
#include <type_traits>
#include <boost/type_erasure/operators.hpp>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/any_cast.hpp>
#include <boost/type_erasure/relaxed.hpp>
#include <boost/mpl/vector.hpp>
namespace te = boost::type_erasure;
// A class to store attributes of any type by name.
class Attributes
{
public:
using Key = std::string;
// A type that can store any value (similar to std::any).
using AnyValue = te::any< boost::mpl::vector<
te::copy_constructible<>,
te::destructible<>,
te::typeid_<>,
te::relaxed
>>;
// Overload for all types that ain't strings.
template< typename T >
std::enable_if_t< !std::is_convertible<T, std::string>::value,
void > SetAttr(Key const& name, T&& value)
{
m_attr.insert_or_assign(name, std::forward<T>(value));
}
// Convert to std::string for all convertible types
// (char pointer and char array included).
template< typename T >
std::enable_if_t< std::is_convertible<T, std::string>::value,
void > SetAttr(Key const& name, T&& value)
{
m_attr.insert_or_assign(name, std::string(std::forward<T>(value)));
}
template< typename T >
T GetAttr(Key const& name) const
{
return te::any_cast<T>(m_attr.at(name));
}
private:
std::unordered_map<Key, AnyValue> m_attr;
};
下面的例子演示瞭如何不同類型的字符串可以傳遞給Attributes::SetAttr()
並通過Attributes::GetAttr<std::string>()
一般查詢:
using namespace std;
Attributes a;
// Works even w/o special care.
a.SetAttr("key1", string("foo"));
// Without the SetAttr() overload for strings, user would have to remind
// to cast back to char const* when calling MyClass::GetAttr().
a.SetAttr("key2", "bar");
// Without the SetAttr() overload for strings, a later call to GetAttr()
// would cause a crash because we are passing pointers to temporary objects.
{
// test arrays
char temp1[] = { 'b', 'a', 'z', 0 };
a.SetAttr("key3", temp1);
char const temp2[] = { 'b', 'i', 'm', 0 };
a.SetAttr("key4", temp2);
// test pointers
a.SetAttr("key5", &temp1[0]);
a.SetAttr("key6", &temp2[0]);
}
try
{
// When getting a string attribute we no longer have to care about how it was
// passed to SetAttr(), we can simply cast to std::string in all cases.
cout << "'" << a.GetAttr<string>("key1") << "'" << endl;
cout << "'" << a.GetAttr<string>("key2") << "'" << endl;
cout << "'" << a.GetAttr<string>("key3") << "'" << endl;
cout << "'" << a.GetAttr<string>("key4") << "'" << endl;
cout << "'" << a.GetAttr<string>("key5") << "'" << endl;
cout << "'" << a.GetAttr<string>("key6") << "'" << endl;
}
// boost::type_erasure::bad_any_cast or std::out_of_range
catch(std::exception& e)
{
cout << "Error: " << e.what() << endl;
}
Live Demo.
你應該提到的是'「你好」 s'作品僅在C++ 14及以上版本中。 –
我希望沒有「s」後綴,因爲它只是調用'std :: string'構造函數的語法糖。我們的目的是爲了避免my_any用戶分配字符串指針時出現錯誤,假定會做一個副本(比如'std :: string'),然後指針的生命週期在'my_any'之前結束閱讀,導致崩潰。 – zett42
@ zett42如果沒有首先檢查它或證明它是那種類型,你永遠不應該得到任何類型的類型。你永遠不應該把一個類型放在任何一箇中,而不要讓這個人把它拿出來證明它是什麼。如果你想要一個枚舉類型的集合,請考慮variant類型。如果你想支持任何必須可以轉換爲字符串的任何東西(或者甚至必須可選地將其轉換爲帶有運行時檢查失敗的字符串),那麼你可能會這樣做。 – Yakk