Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ c++;更改函数常量的模板_C++_Templates_C++11_Sfinae - Fatal编程技术网

C++ c++;更改函数常量的模板

C++ c++;更改函数常量的模板,c++,templates,c++11,sfinae,C++,Templates,C++11,Sfinae,我感兴趣的是设计一个模板接口,其中函数的常量和返回类型本身会随着模板参数的变化而变化。我已经成功地对返回类型执行了以下操作 template<typename T, bool canChange> struct Changable{}; template<typename T> struct Changable<T,true> { typedef T type; }; template<typename T> struct Changa

我感兴趣的是设计一个模板接口,其中函数的常量和返回类型本身会随着模板参数的变化而变化。我已经成功地对返回类型执行了以下操作

template<typename T, bool canChange>
struct Changable{};

template<typename T>
struct Changable<T,true>
{
    typedef T type;
};

template<typename T>
struct Changable<T,false>
{
    typedef const T type;
};

template<typename T, bool canChange>
struct Data{
    typedef typename Changable<T,canChange>::type DataType;        
    DataType m_data; //< This makes it const/non-const at compile time.

    // This function will also make the return type const/non-const 
    // at compile time. 
    DataType& GetDataRef(){ return m_data;} 

    //However, it seems to me that I still need a second function 
    //with an explicit "const", which I can't seem to avoid.
    DataType& GetDataRef()const{return m_data;}
};
模板
结构可变{};
模板
结构可更改
{
T型;
};
模板
结构可更改
{
typedef const T type;
};
模板
结构数据{
typedef typename Changable::type数据类型;
数据类型m_data;//<这使得它在编译时是常量/非常量。
//此函数还将使返回类型为常量/非常量
//在编译时。
DataType&GetDataRef(){return m_data;}
//然而,在我看来,我仍然需要第二个函数
//用一个明确的“const”,我似乎无法避免。
DataType&GetDataRef()常量{return m_data;}
};

我是否可以使用一些SFINAE魔法在编译时避免使用两个常量/非常量函数?在这里启用if是理想的,但在我看来const不是一种类型,这种方法可能不起作用。有什么建议吗?

也许这样可以解决这个问题:

#include <type_traits>
#include <iostream>

template<typename T, bool canChange>
struct Changable: std::false_type { using type = const T; };

template<typename T>
struct Changable<T, true>: std::true_type { using type = std::decay_t<T>; };

template<typename T, bool canChange>
struct Data {
    using DataTraits = Changable<T, canChange>;

private:
    template<typename U>
    std::enable_if_t<U::value, typename U::type&>
    GetDataRefImpl() { std::cout << "non const" << std::endl; return m_data; }

    template<typename U>
    std::enable_if_t<not U::value, typename U::type&>
    GetDataRefImpl() const { std::cout << "const" << std::endl; return m_data; }

public:
    typename DataTraits::type m_data{};

    typename DataTraits::type& GetDataRef() { return GetDataRefImpl<DataTraits>(); }
    typename DataTraits::type& GetDataRef() const { return GetDataRefImpl<DataTraits>(); }
};

int main() {
    Data<int, true> d1;
    Data<int, false> d2;
    d1.GetDataRef();
    d2.GetDataRef();
}
#包括
#包括
模板
struct Changable:std::false_type{using type=const T;};
模板
struct Changable:std::true_type{using type=std::decation_t;};
模板
结构数据{
使用DataTraits=Changable;
私人:
模板
std::如果启用,则启用

GetDataRefImpl(){std::cout下面是一个基于继承的示例:

#include <type_traits>
#include <iostream>

template<typename T, bool canChange>
struct Changable { using type = const T; };

template<typename T> 
struct Changable<T, true> { using type = std::decay_t<T>; };

template<typename, typename, bool>
struct Base;

template<typename D, typename T>
struct Base<D, T, true> {
    using DataType = typename Changable<T, true>::type;
    DataType& GetDataRef() { std::cout << "non-const" << std::endl; return static_cast<D*>(this)->m_data; }
};

template<typename D, typename T>
struct Base<D, T, false> {
    using DataType = typename Changable<T, false>::type;
    DataType& GetDataRef() const { std::cout << "const" << std::endl; return static_cast<const D*>(this)->m_data; }
};

template<typename T, bool canChange>
struct Data: Base<Data<T, canChange>, T, canChange> {
    friend class Base<Data<T, canChange>, T, canChange>;
    typename Base<Data<T, canChange>, T, canChange>::DataType m_data{};
    using Base<Data<T, canChange>, T, canChange>::GetDataRef;
};

int main() {
    Data<int, true> d1;
    Data<int, false> d2;
    d1.GetDataRef();
    d2.GetDataRef();
}
#包括
#包括
模板
结构变量{using type=const T;};
模板
结构可更改{using type=std::decation_t;};
模板
结构基础;
模板
结构基{
使用DataType=typename Changable::type;

DataType&GetDataRef(){std::cout我想我应该使用标准库中已有的模板来实现这一点。它不需要继承或任何自定义类

#include <utility>

template<typename T, bool canChange>
struct Data{
    using value_type = T;
    using cv_type = std::conditional_t<canChange, value_type, std::add_const_t<value_type>>;
    using reference = std::add_lvalue_reference_t<cv_type>;
    using const_reference = std::add_lvalue_reference_t<std::add_const_t<cv_type>>;

    Data(T t) : m_data(std::move(t)) {}

    cv_type m_data; //< This makes it const/non-const at compile time.

    // This function will also make the return type const/non-const
    // at compile time.
    reference GetDataRef(){ return m_data;}

    //However, it seems to me that I still need a second function
    //with an explicit "const", which I can't seem to avoid.
    const_reference GetDataRef() const {return m_data;}
};

int main()
{
    Data<int, true> d1 { 10 };
    d1.m_data = 12;
    const Data<int, true>& rd1 = d1;

    auto& a = d1.GetDataRef();
    auto& b = rd1.GetDataRef();
    a = 12;  // compiles fine
//    b= 12; won't compile

    Data<int, false> d2 { 10 };
    const Data<int, false>& rd2 = d2;

    auto& c = d2.GetDataRef();
    auto& d = rd2.GetDataRef();
//    c = 12;  // won't compile
//    d = 12;  // won't compile

}
#包括
模板
结构数据{
使用值_type=T;
使用cv_type=std::conditional_t;
使用reference=std::add\u lvalue\u reference\t;
使用const\u reference=std::add\u lvalue\u reference\t;
数据(T T):m_数据(std::move(T)){
cv_type m_data;//<这使得它在编译时是常量/非常量。
//此函数还将使返回类型为常量/非常量
//在编译时。
引用GetDataRef(){返回m_数据;}
//然而,在我看来,我仍然需要第二个函数
//用一个明确的“const”,我似乎无法避免。
const_reference GetDataRef()const{return m_data;}
};
int main()
{
数据d1{10};
d1.m_数据=12;
常数数据&rd1=d1;
auto&a=d1.GetDataRef();
auto&b=rd1.GetDataRef();
a=12;//编译得很好
//b=12;无法编译
数据d2{10};
常数数据&rd2=d2;
auto&c=d2.GetDataRef();
auto&d=rd2.GetDataRef();
//c=12;//无法编译
//d=12;//无法编译
}
现在谈问题:

我是否可以使用一些SFINAE魔法,在编译时避免使用两个常量/非常量函数

您在这里几乎回答了自己的问题。SFINAE要求在直接上下文中考虑模板参数。这是一种复杂的方式,表示
std::enable\u if
中的表达式必须依赖于某个模板类型

不幸的是,在计算函数
GetDataRef
时,T的模板类型是已知的,因此在这里启用\u对我们没有帮助

因此,如果我们只需要一个版本的
GetDataRef
,那么我们实际上必须求助于从模板类型派生(然后在T的直接上下文中对基类进行求值)

然而,即使在那时也存在一个问题

考虑:

  • Data&x
    这是对包含可变数据的可变容器的引用

  • const Data&y
    这是对包含可变数据的不可变容器的引用

调用
x.GetDataRef()
应该返回对int的可变引用,否则我们会混淆用户


调用
y.GetDataRef()
肯定会返回对int的常量引用,否则,用户可能再次震惊地发现常量对象的一个成员实际上是可变的。

如果您可以使用C++17,请查看
is_const
add_const
remove_const

if constexpr()一起使用
,一个相当优雅的解决方案应该是可能的。

如果不需要重载,为什么不只保留
常量
版本?@KABoissonneault我确实需要这两个版本,因为模板的常量将根据模板参数而变化。为了澄清,我确实希望能够创建某些
数据
结构它可以在以后的阶段进行修改,但有些保证不会更改。因此需要常量/非常量功能。您仍然需要在公共界面中使用常量和非常量功能。它只比我现有的稍好。我一直在寻找类似的问题,现在我越来越相信它可以在公共接口中没有两个函数是不行的。@cpluplusrat你是对的,但是这些方法只不过是内部sfinaed方法的转发器。此外,它们允许你在常量和非常量实例中使用该方法。现在保留了类的常量。另一个解决方案是在pe上专门化该类r-
canChange
base或有一名代表向其转发请求。很好!一如既往无敌!:)@W.F.老实说,你的回答指出了正确的出路。-)这我喜欢:)。谢谢!@cplusplusrat欢迎你。挑战总是好的,我通常会从中学习一些东西。-@skypjack不需要继承。-)Chape无论如何,OP在其接口中只需要一个函数,常量或非常量(