当我重载subsref(下标引用)时,为什么MATLAB会抛出"太多的输出参数"错误?

Mic*_*l A 6 matlab operator-overloading

作为一个玩具示例,我有一个类,它简单地将一个向量或矩阵包装在一个对象中,并包含一个创建时间的时间戳.我试图超载subsref这样

  1. () 引用的工作方式与标准向量和矩阵类型完全相同
  2. {}引用的工作方式与引用完全相同()(换句话说,与单元格无关)
  3. .引用允许我访问对象的私有属性以及非技术属性的其他字段.

码:

classdef TimeStampValue

    properties (Access = private)
        time;
        values;
    end

    methods
        %% Constructor
        function x = TimeStampValue(values)
            x.time = now();
            x.values = values;
        end

        %% Subscripted reference
        function x = subsref(B, S)
            switch S.type
                case '()'
                    v = builtin('subsref', B.values, S);
                    x = TimeStampValue(v);
                case '{}'
                    S.type = '()';
                    v = builtin('subsref', B.values, S);
                    x = TimeStampValue(v);
                case '.'
                    switch S.subs
                        case 'time'
                            x = B.time;
                        case 'values'
                            x = B.values;
                        case 'datestr'
                            x = datestr(B.time);
                    end
            end
        end

        function disp(x)
            fprintf('\t%d\n', x.time)
            disp(x.values)
        end   

    end

end
Run Code Online (Sandbox Code Playgroud)

但是大括号{}引用不起作用.我运行这段代码

clear all
x = TimeStampValue(magic(3));
x{1:2}
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

Error using TimeStampValue/subsref
Too many output arguments.
Error in main (line 3)
x{1:2} 
Run Code Online (Sandbox Code Playgroud)

MException.last 给我这个信息:

identifier: 'MATLAB:maxlhs'
   message: 'Too many output arguments.'
     cause: {0x1 cell}
     stack: [1x1 struct]
Run Code Online (Sandbox Code Playgroud)

这没有用,因为异常堆栈中唯一的东西是包含我在上面运行的三行代码的文件.

我在switch语句的第一行放置了一个断点,subsref但MATLAB从未到达它.

什么交易在这里?两者().引用都按预期工作,那么为什么不{}引用工作呢?

cha*_*pjc 9

当重载大括号{}以返回比平常更多数量的输出参数时,还必须重载numel以返回预期的数字(在本例中为1).更新:R2015b开始,新功能numArgumentsFromSubscript被创建为重载而不是numel.问题仍然存在,但是这个功能应该重载,而不是numel我在下面的原始答案中描述的那样.另请参阅"修改索引方法的nargout和nargin"页面.摘抄:

当类重载时numArgumentsFromSubscript,MATLAB调用此方法而不是numel计算期望的subsref nargout和的参数数量subsasgn nargin.

如果类不重载numArgumentsFromSubscript,MATLAB调用numel计算nargout或的值nargin.

下面是对底层问题的更多解释(需要指定输出参数的数量).


原始答案(numArgumentsFromSubscript代替numelR2015b +使用)

为了在使用花括号进行索引时处理以逗号分隔的输出参数列表的可能性,MATLAB调用numel以根据输入索引的大小确定输出参数的数量(根据MathWorks的答案).如果重载定义中的输出参数subsref数与提供的数字不一致(即小于)numel,则会出现"Too many output arguments"错误.如MathWorks所述:

因此,为了允许大括号索引到您的对象,同时返回大量具有输入大小的参数INCONSISTENT,您将需要重载类目录中的NUMEL函数.

由于x{1:2}通常提供两个输出(X{1},X{2}),因此function x = subsref(B, S)该输入的定义不兼容.解决方案是在类中包含一个简单的numel方法来重载内置函数,如下所示:

function n = numel(varargin)
    n = 1;
end
Run Code Online (Sandbox Code Playgroud)

现在{}索引按预期工作,模仿():

>> clear all % needed to reset the class definition
>> x = TimeStampValue(magic(3));
>> x(1:2)
ans = 
    7.355996e+05
     8     3
>> x{1:2}
ans = 
    7.355996e+05
     8     3
Run Code Online (Sandbox Code Playgroud)

但是,以这种方式重载花括号显然是"我们[MathWorks]不希望客户写的特定类型的代码".MathWorks建议:

如果您设计的类只输出一个参数,则不建议您使用需要重载NUMEL的花括号索引.相反,建议您使用smooth brace()索引.

更新:有趣的是,R2015b发布说明:

在MATLAB发布R2015b之前,MATLAB错误地计算了返回或分配给逗号分隔列表的某些索引表达式的输出subsref和输入的预期参数数subsasgn.

在版本R2015b中,MATLAB正确计算索引表达式所需参数的数量nargout和值nargin.

那么也许现在已经修好了?


想到的另一种解决方案是改变function x = subsref(B, S)function varargout = subsref(B, S)和添加varargout=cell(1,numel(B)); varargout{1} = x;.正如Amro在评论中指出的那样,预先分配单元格是必要的,以避免有关未分配参数的错误.